Data Download


  • My choice of dataset is GSE209552.
  • Note: GSE209552 is single nucleus RNA-seq (snRNA-seq), a varient of scRNA-seq that only sequence nucleus RNA.
# Define global constant
GSE_NUM <- "GSE209552"

GEOmetadb setup

To setup GEOmetadb Tutorial, follow GEOmetadb Tutorial Chapter 3

# Install required package if not installed
if (!requireNamespace("BiocManager", quietly = TRUE))
  install.packages("BiocManager")
if (!requireNamespace("GEOmetadb", quietly = TRUE))
  BiocManager::install("GEOmetadb")
if (!requireNamespace("DBI", quietly = TRUE))
  install.packages("DBI")
if (!requireNamespace("RSQLite", quietly = TRUE))
  install.packages("RSQLite")
if (!requireNamespace("dplyr", quietly = TRUE))
  install.packages("dplyr")
if (!requireNamespace("ggplot2", quietly = TRUE))
  install.packages("ggplot2")

# Attach packages
library(GEOmetadb)
library(DBI)
library(RSQLite)

# Download SQLite database if not exist
if (!file.exists('GEOmetadb.sqlite')) getSQLiteFile()
file.info('GEOmetadb.sqlite')

# Connect to database
con <- dbConnect(SQLite(), 'GEOmetadb.sqlite')

Download whole GSE209552

Download data, follow Identifier Mapping Tutorial. The helper function defined below is modified based on Identifier Mapping Tutorial’s helper function to avoid duplicated download.

# Define helper function
fetch_geo_supp <- function(gse, destdir = "data") {
  # Calculate the actual path where GEOquery puts the files
  target_path <- file.path(destdir, gse)
  
  # Check if directory exists AND contains files
  if (dir.exists(target_path) && length(list.files(target_path)) > 0) {
    message(paste("Data found at", target_path, "- Skipping download."))
    return(invisible(target_path))
  }
  
  # If missing or empty, proceed with download
  message(paste("Downloading", gse, "to", destdir, "..."))
  dir.create(destdir, showWarnings = FALSE, recursive = TRUE)
  GEOquery::getGEOSuppFiles(GEO = gse, baseDir = destdir, makeDirectory = TRUE)
  
  invisible(target_path)
}

# Download
fetch_geo_supp(gse = GSE_NUM)

Construct snRNA-seq only reference list

This GSE contain both snRNA-seq and bulk RNA-seq sample. Removal of bulk RNA-seq is needed to avoid significant batch effect due to their technical difference. Although the number of samples is reduced to 17, it’s still sufficient amount.

Refer to the original paper’s method section, only the snRNA-seq was done by Illumina NextSeq6000 platform, GPL24676. We can use this clue to exclude all bulk RNA-seq.

Since all GSMs of this study are packed in one GSE209552_RAW.tar file in GEO, we can’t indicate which platform to include/exclude at download step. Thus we will construct a list of GPL24676 GSMs for downstream reference by query GEOmetabd.

# Define desired platform
snRNA_platform <- "GPL24676"

# Construct the query
# We join the 'link' table (gse_gsm) with the 'sample' table (gsm) 
# to check the platform (gpl) column.
sql_filter <- paste0(
  "SELECT t1.gsm ",
  "FROM gse_gsm t1 ",
  "JOIN gsm t2 ON t1.gsm = t2.gsm ",
  "WHERE t1.gse = '", GSE_NUM, "' ",
  "AND t2.gpl = '", snRNA_platform, "'"
)

# Get the list of pure snRNA-seq samples
sn_gsm_list <- dbGetQuery(con, sql_filter)$gsm

# View results
print(paste("Found", length(sn_gsm_list), "snRNA-seq samples."))
[1] "Found 17 snRNA-seq samples."
sn_gsm_list
 [1] "GSM6376803" "GSM6376804" "GSM6376805" "GSM6376806"
 [5] "GSM6376807" "GSM6376811" "GSM6376812" "GSM6376813"
 [9] "GSM6376814" "GSM6376815" "GSM6376816" "GSM6376817"
[13] "GSM6376818" "GSM6376819" "GSM6376820" "GSM6376821"
[17] "GSM6376822"

Access data quality

# Install Seurat if not
if (!requireNamespace("Seurat", quietly = TRUE))
  install.packages("Seurat")

Reorganize snRNA-seq files

The raw count data was stored in GSE209552_RAW.tar file, thus need to be unpacked using untar() function.

# Attach packages
library(Seurat)
library(data.table)
library(Matrix)
library(dplyr)
library(stringr)

# Define data paths
tar_file <- "data/GSE209552/GSE209552_RAW.tar"
extract_dir <- "data/GSE209552/raw_files"

# Define unpack helper function
unpack_geo_tar <- function(tar_path, dest_dir) {
  if (dir.exists(dest_dir) && length(list.files(dest_dir)) > 0) {
    message(paste("📂 Extracted files found at", dest_dir, "- Skipping unpack."))
    return(invisible(dest_dir))
  }
  if (!file.exists(tar_path)) {
    stop(paste("❌ Tar file not found:", tar_path))
  }
  message(paste("📦 Unpacking", basename(tar_path), "..."))
  dir.create(dest_dir, showWarnings = FALSE, recursive = TRUE)
  untar(tar_path, exdir = dest_dir)
  message("✅ Unpacking complete.")
  return(invisible(dest_dir))
}

# Run the unpacker
unpack_geo_tar(tar_file, extract_dir)

The unpacked file is very messy, contain all 10x genomics snRNA-seq files, bulk RNA-seq files, subsetted Transposable Elements file, clustered files, and normalized files. Among those, only 10x genomics snRNA-seq is what we need.

To extract, we utilize previously defined sn_gsm_list, loop over it, exclude any file contain _TE_, indicate Transposable Elements, only extract files end with either barcode, matrix, or features. Lastly, trim the file name to barcode, matrix, features only, to meet the expected naming format of Seurat’s Read10X() function.

# Reorganize all snRNA-seq 10x files in to a new directory
dest_dir <- "data/GSE209552/10x_organized"    # Where they should go
dir.create(dest_dir, recursive = TRUE, showWarnings = FALSE)

# --- MAIN LOOP ---
message(paste("🚀 Organizing files for", length(sn_gsm_list), "samples..."))

skipped_count <- 0
moved_count   <- 0

for (gsm in sn_gsm_list) {
  
  # 1. Define the specific destination path for this sample
  sample_subdir <- file.path(dest_dir, gsm)
  
  # 2. CHECK: Does it already exist?
  if (dir.exists(sample_subdir) && length(list.files(sample_subdir)) >= 3) {
    skipped_count <- skipped_count + 1
    next
  }
  
  # 3. Create directory
  dir.create(sample_subdir, showWarnings = FALSE)
  
  # 4. Find source files (Exclude the 'TE' files)
  pattern <- paste0("^", gsm)
  files_to_move <- list.files(extract_dir, pattern = pattern, full.names = TRUE)
  files_to_move <- files_to_move[!grepl("_TE_", files_to_move)]
  
  # Safety Checks
  if (length(files_to_move) == 0) {
    warning(paste("⚠️ Skipping", gsm, "- No source files found."))
    next
  }
  
  # 5. MOVE & RENAME
  # We identify specific files to ensure we rename them correctly for Seurat
  f_matrix   <- files_to_move[grep("matrix", files_to_move, ignore.case = TRUE)]
  f_barcodes <- files_to_move[grep("barcodes", files_to_move, ignore.case = TRUE)]
  f_features <- files_to_move[grep("features|genes", files_to_move, ignore.case = TRUE)]
  
  # Verify we found exactly 1 of each before moving
  if (length(f_matrix) == 1 && length(f_barcodes) == 1 && length(f_features) == 1) {
    
    # Rename Matrix -> matrix.mtx.gz
    file.rename(f_matrix, file.path(sample_subdir, "matrix.mtx.gz"))
    
    # Rename Barcodes -> barcodes.tsv.gz
    file.rename(f_barcodes, file.path(sample_subdir, "barcodes.tsv.gz"))
    
    # Rename Genes/Features -> features.tsv.gz
    file.rename(f_features, file.path(sample_subdir, "features.tsv.gz"))
    
    message(paste("  ✅ Standardized & Moved:", gsm))
    moved_count <- moved_count + 1
    
  } else {
    warning(paste("  ⚠️ Skipping", gsm, "- Could not find unique 10x triplet to standardize."))
  }
}

# --- SUMMARY ---
message("🎉 Organization Complete.")
message(paste("  🔹 Samples moved & standardized:", moved_count))
message(paste("  🔸 Samples skipped (already done):", skipped_count))

Load snRNA-seq files into R using Seurat

Loading of expression data is done by iteratively call Seurat’s Read10X() and CreateSeuratObject() function over all GSMs. They cooperatively first read a 10X genomics structured file and create a Seurat Object, which in the basic unit for any data manipulation in Seurat.

# Get the list of sample subdirectories
sample_dirs <- list.dirs(dest_dir, recursive = FALSE)

# Define a list storing seurat obj of all GSMs
seurat_list <- list()

message(paste("🚀 Loading", length(sample_dirs), "samples..."))
for (dir in sample_dirs) {
  # Extract GSM ID from the folder name (e.g., "GSM6385438")
  gsm_id <- basename(dir)
  
  # A. Read 10x Data
  # Because you renamed the files, Read10X finds them automatically!
  counts <- Read10X(data.dir = dir)
  
  # B. Create Seurat Object
  seurat_list[[gsm_id]] <- CreateSeuratObject(
    counts = counts, 
    project = gsm_id, 
    min.cells = 0, 
    min.features = 0
  )
  
  message(paste("  ✅ Loaded:", gsm_id))
}
length(seurat_list)
[1] 17

However, the metadata (contain condition information) need to fetched from GEOmetadb, and extract from GSM table’s characteristics_ch1 column, and then insert into corresponding Seurat object’s meta.data layer.

# Load GSMs metadata from GEOmetadb
gsm_list_string <- paste(paste0("'", sn_gsm_list, "'"), collapse = ",")
sql <- paste0(
  "SELECT gsm, title, characteristics_ch1 ",
  "FROM gsm ",
  "WHERE gsm IN (", gsm_list_string, ")"
)
metadata <- dbGetQuery(con, sql)

# Merge the condition metadata to corresponding Seurat obj's metadata
metadata$Condition <- sub(".*condition:\\s*([^;]+).*", "\\1", metadata$characteristics_ch1, ignore.case = TRUE)

# Loop and assign
for (gsm in names(seurat_list)) {
  # Find the condition for this GSM
  val <- metadata$Condition[metadata$gsm == gsm]
  
  # Assign to Seurat object (handling missing values)
  if (length(val) > 0) {
    seurat_list[[gsm]]$Condition <- val
  } else {
    seurat_list[[gsm]]$Condition <- "Unknown"
  }
}

# Final Merge all samples into one seurat obj
combined_seurat <- merge(
 x = seurat_list[[1]], 
 y = seurat_list[-1], 
 add.cell.ids = names(seurat_list), 
 project = "GSE209552"
)
# Verify
table(combined_seurat$Condition)

control     tbi 
  10666   13955 
# Delete `seurat_list` to free memory
rm(seurat_list)

Data exploriation

Plot the following statistics:

  • Number of unique identifier (infer coverage)
  • Number of total amount of identifier (infer depth)
  • Number of samples
  • Number of cells
library(ggplot2)
library(patchwork)

theme_set(theme_classic(base_size = 26))

# 1. Coverage (nFeature_RNA)
p_cov <- VlnPlot(
  combined_seurat, 
  features = "nFeature_RNA", 
  group.by = "Condition", 
  pt.size = 0
) + 
  ggtitle("Coverage") + 
  ylab("nFeature_RNA")

# 2. Depth (nCount_RNA) with adjusted axis
p_depth <- VlnPlot(
  combined_seurat, 
  features = "nCount_RNA", 
  group.by = "Condition", 
  pt.size = 0
) + 
  ggtitle("Depth") + 
  ylab("nCount_RNA") +
  scale_y_continuous(limits = c(0, 50000)) 

# Combine them to recreate p_metrics
p_metrics <- p_cov | p_depth

# Number of cells among conditions
meta_data <- combined_seurat@meta.data

# A. Bar plot: Total Number of Cells per Condition
p_cells <- ggplot(meta_data, aes(x = Condition, fill = Condition)) +
  geom_bar(color = "black") +
  geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Cells per Group", y = "Number of Cells", x = NULL) +
  theme(legend.position = "none")

# Number of samples among conditions
p_samples <- meta_data %>%
  group_by(Condition) %>%
  summarize(Sample_Count = n_distinct(orig.ident)) %>%
  ggplot(aes(x = Condition, y = Sample_Count, fill = Condition)) +
  geom_bar(stat = "identity", color = "black") +
  geom_text(aes(label = Sample_Count), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Samples (GSMs) per Group", y = "Number of Samples (GSMs)", x = NULL) +
  theme(legend.position = "none")

# Merge plots
final_plot <- p_metrics / (p_samples | p_cells)

# Adjust font size
final_plot <- final_plot & theme_classic(base_size = 16)
final_plot

Figure 1: Overall statistics of dataset GSE209552 grouped by condition (Control v.s. tbi). The upper-left panel indicate the number of unique identifier; The upper-right panel indicate the total amount of identifier, the maximum amount of identifier was limited up to 50000 for the sake of aesthetics; The lower-left panel indicate number of samples (GSMs); The lower-right panel indicate the number of cells”; X-axis of all four panel indicate condition.

Generally, control group have higher depth and coverage, but less sample and cells.

Then plot the statistics below per sample (GSM):

  • Number of unique identifier (infer coverage)
  • Number of total amount of identifier (infer depth)
  • Number of cells

# Define the vertical line style once to keep it consistent
vertical_line <- geom_vline(xintercept = 5.5, linetype = "dashed", color = "red", size = 1)

# 1. Coverage (nFeature_RNA)
p_cov <- VlnPlot(
  combined_seurat, 
  features = "nFeature_RNA", 
  group.by = "orig.ident", 
  pt.size = 0
) + 
  ggtitle("Coverage") + 
  ylab("nFeature_RNA") +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none",
    axis.title.x = element_blank() # Often cleaner to remove x-label if text is rotated
  ) + vertical_line

# 2. Depth (nCount_RNA) with adjusted axis
p_depth <- VlnPlot(
  combined_seurat, 
  features = "nCount_RNA", 
  group.by = "orig.ident", 
  pt.size = 0
) + 
  ggtitle("Depth") + 
  ylab("nCount_RNA") +
  scale_y_continuous(limits = c(0, 50000)) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none",
    axis.title.x = element_blank()
  ) + vertical_line

# A. Bar plot: Total Number of Cells per Sample
meta_data <- combined_seurat@meta.data # Ensure meta_data is defined
p_cells <- ggplot(meta_data, aes(x = orig.ident, fill = orig.ident)) +
  geom_bar(color = "black") +
  geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Cells per Sample", y = "Number of Cells", x = NULL) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none"
  ) +
  coord_fixed(ratio = 0.0005)  +
  scale_y_continuous(limits = c(0, 6000)) + vertical_line

# Merge plots
final_plot <- p_cov / p_depth / p_cells + 
  plot_layout(heights = c(1, 1, 1))

# Adjust font size (and re-apply theme elements if needed by the base theme)
final_plot <- final_plot & theme_classic(base_size = 16) & 
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none"
  )

final_plot

Figure 2: Overall statistics per sample (GSM). The top panel indicate number of unique identifier; The middle panel indicate number of total identifier; The bottom panel indicate number of cells; X-axis of all three panel indicate samples (GSMs); The vertical red dash line separate control (left side of red line), and tbi (right side of red line).

In general, the sequence depth and coverage is more consistence among control group. Tbi group has more variated depth and coverage with GSM637681 being the sample of lowest depths and coverage. The number of cells within different sample also varies, from highest 3708 (GSM6376822) to lowest 358 (GSM6376820). This imbalance in cell number may result in downstream analysis dominate by sample which contain more cells.

Another common quality control step in scRNA-seq is to remove damaged cells. When damaged cells were sequenced or damaged during sequencing, the expression intensity will scaled down due to leaked RNA, thus significantly bias downstream clustering, annotation, and differential gene expression analysis. A common method to conduct this filtering is using mitochondrial transcript percentage, based on this idea that if a cell has leaked RNA, the mitochondrial transcript percentage will be relatively higher since mitochondria is relatively large and less likely to leak. Empirically, the threshold is set to be 5% ~ 10%

However, when it comes to snRNA-seq, mitochondrial transcript percentage is expected to be 0% because of the exclusion of cytoplasm, and any none-zero mitochondrial transcript percentage indicate incomplete exclusion of cytoplasm Amezquita et al, 2020. Thus we may want to choice a more strict threshold for snRNA-seq, like 1% ~ 2%, to match the wet-lab design while tolerate low-quality cell to certain extent.

# Use seurat PercentageFeatureSet() function
# Create a new entry in meta.data layer storing mitochondrial transcript percentage
combined_seurat[["percent.mt"]] <- PercentageFeatureSet(combined_seurat, pattern = "^MT-")

# Defin a horizontal line at Y == 5 indicate empirical mito% threashold
horizontal_line <- geom_hline(yintercept = 10, linetype = "dashed", color = "red", size = 1)

# Percent MT per CONDITION (Control vs TBI)
p_condition <- VlnPlot(combined_seurat, features = c("percent.mt"), group.by = "Condition", pt.size = 0) +
  theme(
    axis.title.x = element_blank(), 
    legend.position = "none" # Hide legend since x-axis already shows the names
  ) + 
  ggtitle("Mitochondrial % by Condition") + 
  horizontal_line + 
  ylab("Mitochondrial Transcript (%)") + 
  annotate("text", x = 0.5, y = 12, label = "mito % = 10", color = "black", hjust = 0, size = 5)

# Percent MT per GSM
p_sample <- VlnPlot(combined_seurat, features = c("percent.mt"), group.by = "orig.ident", pt.size = 0) +
  theme(
    axis.title.x = element_blank(), 
    # Rotate x-axis labels 45 degrees so they don't overlap
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none" # Hide legend since x-axis already shows the names
  ) + vertical_line + 
  ggtitle("Mitochondrial % by Sample") + 
  horizontal_line + 
  ylab("Mitochondrial Transcript (%)") + 
  annotate("text", x = 0.5, y = 12, label = "mito % = 10", color = "black", hjust = 0, size = 5)

# 4. Combine them
final_mt_plot <- p_condition / p_sample
final_mt_plot

Figure 3: Mitochondrial transcript percentage grouped by GSMs. The Y-axis indicate mitochondrial transcript percentage; The X-axis of top panel indicate condition (Control v.s.); The X axis of bottom panel indicate GSM samples; The horizontal red dash line indicate empirical Mitochondrial transcript percentage threshold = 10%; The vertical red dash line at bottom panel separate control (left side of red line), and treatment (right side of red line).

However, the Mitochondrial transcript percentage is way higher than expected in all samples and the original paper didn’t assess the Mitochondrial transcript percentage at all. One clue that may explain this unexpected high Mitochondrial transcript percentage from their Limitations of the study is control tissues were post-mortem sample and tbi tissues were surgically evacuated rare case post-impact. Thus the integrity of samples were not at ideal condition and expected to contain damaged cells.

On the other hands, mitochondrial transcript percentage aligned with previous depth and coverage among samples, where control group tend to be more consistent. A hypothesis is that Traumatic Brain Injury (TBI) is a broad term that include various brain damage result from external force, and the intensity and exact pathology may vary among different tbi sample, reflected by sequence depth and coverage. Noticeably, GSM6376814 has relatively higher mitochondrial transcript percentage, and recall that it’s also the sample having lowest depth and coverage, which might imply existing technical variance and/or batch effect that could bias downstream analysis.

Mapping to HUGO symbols

Check the current identifier

head(rownames(combined_seurat))
[1] "MIR1302-2HG" "FAM138A"     "OR4F5"       "AL627309.1" 
[5] "AL627309.3"  "AL627309.2" 

At a glance, it’s HUGO symbol. To verify, we can query those symbol against org.Hs.eg.db.

# Install org.Hs.eg.db
if (!requireNamespace("org.Hs.eg.db", quietly = TRUE)) {
  BiocManager::install("org.Hs.eg.db")
}

# Attach org.Hs.eg.db to current session
library(org.Hs.eg.db)

# Check how many of them exist in the official symbol database
valid_symbols <- mapIds(org.Hs.eg.db, 
                        keys = rownames(combined_seurat), 
                        column = "SYMBOL", 
                        keytype = "SYMBOL", 
                        multiVals = "first")

# Calculate the percentage of matches
sum(!is.na(valid_symbols)) / length(rownames(combined_seurat)) * 100
[1] 100

100% valid symbols confirmed the symbol is acually HUGO symbol.

Cleaning

First we remove GSM6376814, since it has the lowest depth, coverage, and highest mitochondrial transcript percentage, thus pron to contaminate downstream analysis.

combined_seurat <- subset(combined_seurat, subset = orig.ident != "GSM6376814")

Based on Seurat and Heumos et al, 2023, they simply applied hard threshold on mitochondrial transcript percentage, 5% and 8%, at their quality control step. Additionally, refer to previous Figure 3, I empirically choice the threshold to be 10%.

combined_seurat <- subset(combined_seurat, subset = percent.mt <= 10)

In the end, 2887/24621 = 11.7% cells were removed. Compare with original paper, they removed 9511/24621 = 38.6% cells. I suppose part of the reason they apply such restrict quality control is they are aware of the low integrity of samples.

# 1. Mito % Plot (After Cleaning)
p_mito_clean <- VlnPlot(
  combined_seurat, 
  features = "percent.mt", 
  group.by = "orig.ident", 
  pt.size = 0
) + 
  vertical_line + 
  ggtitle("Mitochondrial % per Sample (After Cleaning)") + 
  ylab("Mitochondrial Transcript (%)") + 
  scale_y_continuous(limits = c(0, 10)) + # Zoom in since max is now <= 10
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1), 
    legend.position = "none",
    axis.title.x = element_blank()
  ) +
  coord_fixed(ratio = 0.5)

# 2. Total Cells Plot (Your existing code)
meta_data <- combined_seurat@meta.data 
p_cells <- ggplot(meta_data, aes(x = orig.ident, fill = orig.ident)) +
  geom_bar(color = "black") +
  geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Cells per Sample (After Cleaning)", y = "Number of Cells", x = NULL) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none"
  ) +
  coord_fixed(ratio = 0.001) +
  scale_y_continuous(limits = c(0, 4000)) + 
  vertical_line

# 3. Combine them vertically
final_clean_plot <- p_mito_clean / p_cells
final_clean_plot

Figure 4: Number of cells after cleaning grouped by GSMs. The Y-axis indicate number of cells; The X-axis indicate GSMs; The vertical red dash line separate control (left side of red line), and tbi (right side of red line).

Normalization

Differ from RNA-seq and bulk RNA-seq normalization we covered in class, sc/snRNA-seq data is zero-inflated, most of gene’s counts is 0 in most of cells. Thus two of the method we covered in class, TMM and RLE, are not suitable for sc/snRNA-seq because they both assume most of the gene are not differentially expressed, zero-inflated counts data will yield inflated fold change when ever an algorithm is trying to calculate fold change (Lytal et al, 2020).

Library size based Log-Normalization

The simple normalization by library size still work as intended and won’t affect by inflated zeros and widely utilized in sing-cell transcriptome field as a baseline (Ge et al, 2025, Lytal et al, 2020, Amezquita et al, 2019). In Seurat, it’s wrapped as NormalizeData() function. Note the default scale.factor is set to be 10000 to account for sc/snRNA-seq’s relative small reads per cell compare with bulk RNA-seq.

For visualization, I choice first randomly sample one cell from each GSM, exclude genes with 0 counts, and plot the pre and post normalized counts on Y-axis, sampled cell on X-axis. The reason of no not plot normalized counts per GSM is because in sc/snRNA-seq, the counts is normalized by the total reads of a cell rather than GSM, thus plot normalized counts per GSM won’t be so meaningful.

library(tidyr)
library(tibble)

# Collapse the split sample layers into a single unified 'counts' layer
combined_seurat <- JoinLayers(combined_seurat)

# 1. Randomly sample exactly ONE cell per GSM (orig.ident)
set.seed(42) # Set seed for reproducibility
sampled_cells <- combined_seurat@meta.data %>%
  rownames_to_column("Cell_ID") %>%
  group_by(orig.ident) %>%
  sample_n(1) %>%
  pull(Cell_ID)

# Subset the Seurat object to only contain these representative cells
single_cell_seurat <- subset(combined_seurat, cells = sampled_cells)

# 2. Extract Pre-Normalization Data (Raw Counts)
raw_matrix <- as.matrix(GetAssayData(single_cell_seurat, layer = "counts"))

df_raw <- as.data.frame(raw_matrix) %>%
  pivot_longer(cols = everything(), names_to = "Cell_ID", values_to = "Count") %>%
  filter(Count > 0) %>%
  mutate(Sample = single_cell_seurat@meta.data[Cell_ID, "orig.ident"])

# Plot Pre-Normalization
p_pre_norm <- ggplot(df_raw, aes(x = Sample, y = Count, fill = Sample)) +
  geom_violin(scale = "width", color = "black", alpha = 0.8) + 
  ggtitle("Pre-Normalization: 1 Cell per GSM (Raw Counts)") + 
  ylab("Expression (Linear Counts)") +
  theme_classic() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none",
    axis.title.x = element_blank()
  ) + vertical_line + 
  scale_y_continuous(limits = c(0, 200))

# 3. Normalization 
combined_seurat <- NormalizeData(combined_seurat, normalization.method = "LogNormalize", scale.factor = 10000)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
# Re-subset to get the updated normalized data for those exact same cells
single_cell_seurat <- subset(combined_seurat, cells = sampled_cells)

# 4. Extract Post-Normalization Data (Log-Normalized)
# NormalizeData automatically outputs to a unified 'data' layer, so this is safe
norm_matrix <- as.matrix(GetAssayData(single_cell_seurat, layer = "data"))

df_norm <- as.data.frame(norm_matrix) %>%
  pivot_longer(cols = everything(), names_to = "Cell_ID", values_to = "Normalized_Count") %>%
  filter(Normalized_Count > 0) %>%
  mutate(Sample = single_cell_seurat@meta.data[Cell_ID, "orig.ident"])

# Plot Post-Normalization
p_post_norm <- ggplot(df_norm, aes(x = Sample, y = Normalized_Count, fill = Sample)) +
  geom_violin(scale = "width", color = "black", alpha = 0.8) + 
  ggtitle("Post-Normalization: 1 Cell per GSM (Log-Normalized)") + 
  ylab("Expression (Log1p scale)") + 
  theme_classic() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none",
    axis.title.x = element_blank()
  ) + vertical_line + 
  scale_y_continuous(limits = c(0, 6))

# 5. Combine plots
final_norm_plot <- p_pre_norm / p_post_norm
final_norm_plot

Figure 5: Pre and post normalized counts per cell ( randomly sample 1 cell per GSM). The Y-axis of upper panel indicate raw counts; The Y-axis of bottom panel indicate Log-Normalized counts; The X-axis indicate the GSMs that cell is sampled from; The vertical red dash line separate control (left side of red line), and tbi (right side of red line).

The normalized counts yield interesting vases shape, a flat and wide base with numbers of beads attached upon. The flat base is likely because of the zero-inflated nature of sc/snRNA-seq, even though I exclude all gene that has count == 0, most gene still have counts close to zero, following negative binomial distribution. However, I don’t have a well constructed hypothesis to explain those beads attached, maybe because the raw input (raw counts) in discrete, the log-Normalized counts will also be discrete. The relative height of the base is reflecting sequence depth of those cell. When those counts close to zero were normalized by total read of a cell, the larger the total read, the smaller the normalized count. Thus the higher the base, the lower the sequence depth.

Note : X-axis is the GSM those cells sampled from, not GSM itself. So we can’t infer sequence depth of GSM from Figure 5.

Obtain normalized dataframe

NormalizeData()stored normalized counts in data layer. Simply call Seurat::GetAssayData() on data layer to obtain normalized counts, and convert to data.frame use as.data.frame().

# extract normalized data
norm_matrix <- GetAssayData(combined_seurat, layer = "data")

# Check dimensions (Genes x Cells)
dim(norm_matrix)
[1] 33538 21734
# Check rowname
head(rownames(norm_matrix))
[1] "MIR1302-2HG" "FAM138A"     "OR4F5"       "AL627309.1" 
[5] "AL627309.3"  "AL627309.2" 
# Check colname
head(colnames(norm_matrix))
[1] "GSM6376803_AAACCCAGTTTCGGCG-1"
[2] "GSM6376803_AAACGAAAGACCATTC-1"
[3] "GSM6376803_AAACGAACAGAGGTAC-1"
[4] "GSM6376803_AAACGAACATGTGGTT-1"
[5] "GSM6376803_AAACGAATCGGATACT-1"
[6] "GSM6376803_AAACGAATCTGTCCGT-1"
# convert to data.frame
# Comment out to skip due to memory constrain
# norm_df <- as.data.frame(norm_matrix)
# Check dimensions (Genes x Cells)
# dim(norm_df)

The final result is in dgCMatrix instead of data.frame due to memory constrain. Convert to data.frame require additional ~5.4 GB of RAM.

LS0tCnRpdGxlOiAiQXNzaWdubWVudCAjMSIKYXV0aG9yOiAiSmlhcWkgTWEiCmRhdGU6ICIyMDI2LTAyLTA2IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogbm9uZQogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgZGZfcHJpbnQ6IHBhZ2VkCmJpYmxpb2dyYXBoeTogQkNCNDIwLmJpYgpsaW5rLWNpdGF0aW9uczogdHJ1ZQotLS0KCiMgRGF0YSBEb3dubG9hZAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotICAgTXkgY2hvaWNlIG9mIGRhdGFzZXQgaXMgYEdTRTIwOTU1MmAuCi0gICBOb3RlOiBgR1NFMjA5NTUyYCBpcyBzaW5nbGUgbnVjbGV1cyBSTkEtc2VxIChgc25STkEtc2VxYCksIGEgdmFyaWVudCBvZiBgc2NSTkEtc2VxYCB0aGF0IG9ubHkgc2VxdWVuY2UgbnVjbGV1cyBgUk5BYC4KCmBgYHtyfQojIERlZmluZSBnbG9iYWwgY29uc3RhbnQKR1NFX05VTSA8LSAiR1NFMjA5NTUyIgpgYGAKCiMjIEdFT21ldGFkYiBzZXR1cAoKVG8gc2V0dXAgW0dFT21ldGFkYiBUdXRvcmlhbF0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9HRU9tZXRhZGIuaHRtbCksIGZvbGxvdyBbR0VPbWV0YWRiIFR1dG9yaWFsIENoYXB0ZXIgM10oaHR0cHM6Ly9iY2I0MjAtMjAyNi5naXRodWIuaW8vRXhlcmNpc2VfRmluZGluZ19FeHByZXNzaW9uX2RhdGEvc2V0dGluZy11cC1nZW9tZXRhZGIuaHRtbCkKCmBgYHtyfQojIEluc3RhbGwgcmVxdWlyZWQgcGFja2FnZSBpZiBub3QgaW5zdGFsbGVkCmlmICghcmVxdWlyZU5hbWVzcGFjZSgiQmlvY01hbmFnZXIiLCBxdWlldGx5ID0gVFJVRSkpCiAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkdFT21ldGFkYiIsIHF1aWV0bHkgPSBUUlVFKSkKICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiR0VPbWV0YWRiIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJEQkkiLCBxdWlldGx5ID0gVFJVRSkpCiAgaW5zdGFsbC5wYWNrYWdlcygiREJJIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJSU1FMaXRlIiwgcXVpZXRseSA9IFRSVUUpKQogIGluc3RhbGwucGFja2FnZXMoIlJTUUxpdGUiKQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoImRwbHlyIiwgcXVpZXRseSA9IFRSVUUpKQogIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJnZ3Bsb3QyIiwgcXVpZXRseSA9IFRSVUUpKQogIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQoKIyBBdHRhY2ggcGFja2FnZXMKbGlicmFyeShHRU9tZXRhZGIpCmxpYnJhcnkoREJJKQpsaWJyYXJ5KFJTUUxpdGUpCgojIERvd25sb2FkIFNRTGl0ZSBkYXRhYmFzZSBpZiBub3QgZXhpc3QKaWYgKCFmaWxlLmV4aXN0cygnR0VPbWV0YWRiLnNxbGl0ZScpKSBnZXRTUUxpdGVGaWxlKCkKZmlsZS5pbmZvKCdHRU9tZXRhZGIuc3FsaXRlJykKCiMgQ29ubmVjdCB0byBkYXRhYmFzZQpjb24gPC0gZGJDb25uZWN0KFNRTGl0ZSgpLCAnR0VPbWV0YWRiLnNxbGl0ZScpCmBgYAoKIyMgRG93bmxvYWQgd2hvbGUgR1NFMjA5NTUyCgpEb3dubG9hZCBkYXRhLCBmb2xsb3cgW0lkZW50aWZpZXIgTWFwcGluZyBUdXRvcmlhbF0oaHR0cHM6Ly9iY2I0MjAtMjAyNi5naXRodWIuaW8vRXhhbXBsZV9TdHVkZW50L2lkX21hcHBpbmcvZ2V0dGluZy1kYXRhLWZyb20tZ2VvLmh0bWwpLiBUaGUgaGVscGVyIGZ1bmN0aW9uIGRlZmluZWQgYmVsb3cgaXMgbW9kaWZpZWQgYmFzZWQgb24gW0lkZW50aWZpZXIgTWFwcGluZyBUdXRvcmlhbCdzIGhlbHBlciBmdW5jdGlvbl0oaHR0cHM6Ly9naXRodWIuY29tL2JjYjQyMC0yMDI2L0V4YW1wbGVfU3R1ZGVudC9ibG9iL21haW4vaW5fY2xhc3NfZXhlcmNpc2VzL2lkX21hcHBpbmcvZmV0Y2hfZ2VvX3N1cHAuUikgdG8gYXZvaWQgZHVwbGljYXRlZCBkb3dubG9hZC4KCmBgYHtyfQojIERlZmluZSBoZWxwZXIgZnVuY3Rpb24KZmV0Y2hfZ2VvX3N1cHAgPC0gZnVuY3Rpb24oZ3NlLCBkZXN0ZGlyID0gImRhdGEiKSB7CiAgIyBDYWxjdWxhdGUgdGhlIGFjdHVhbCBwYXRoIHdoZXJlIEdFT3F1ZXJ5IHB1dHMgdGhlIGZpbGVzCiAgdGFyZ2V0X3BhdGggPC0gZmlsZS5wYXRoKGRlc3RkaXIsIGdzZSkKICAKICAjIENoZWNrIGlmIGRpcmVjdG9yeSBleGlzdHMgQU5EIGNvbnRhaW5zIGZpbGVzCiAgaWYgKGRpci5leGlzdHModGFyZ2V0X3BhdGgpICYmIGxlbmd0aChsaXN0LmZpbGVzKHRhcmdldF9wYXRoKSkgPiAwKSB7CiAgICBtZXNzYWdlKHBhc3RlKCJEYXRhIGZvdW5kIGF0IiwgdGFyZ2V0X3BhdGgsICItIFNraXBwaW5nIGRvd25sb2FkLiIpKQogICAgcmV0dXJuKGludmlzaWJsZSh0YXJnZXRfcGF0aCkpCiAgfQogIAogICMgSWYgbWlzc2luZyBvciBlbXB0eSwgcHJvY2VlZCB3aXRoIGRvd25sb2FkCiAgbWVzc2FnZShwYXN0ZSgiRG93bmxvYWRpbmciLCBnc2UsICJ0byIsIGRlc3RkaXIsICIuLi4iKSkKICBkaXIuY3JlYXRlKGRlc3RkaXIsIHNob3dXYXJuaW5ncyA9IEZBTFNFLCByZWN1cnNpdmUgPSBUUlVFKQogIEdFT3F1ZXJ5OjpnZXRHRU9TdXBwRmlsZXMoR0VPID0gZ3NlLCBiYXNlRGlyID0gZGVzdGRpciwgbWFrZURpcmVjdG9yeSA9IFRSVUUpCiAgCiAgaW52aXNpYmxlKHRhcmdldF9wYXRoKQp9CgojIERvd25sb2FkCmZldGNoX2dlb19zdXBwKGdzZSA9IEdTRV9OVU0pCmBgYAoKIyMgQ29uc3RydWN0IHNuUk5BLXNlcSBvbmx5IHJlZmVyZW5jZSBsaXN0CgpUaGlzIEdTRSBjb250YWluIGJvdGggYHNuUk5BLXNlcWAgYW5kIGBidWxrIFJOQS1zZXFgIHNhbXBsZS4gUmVtb3ZhbCBvZiBidWxrIFJOQS1zZXEgaXMgbmVlZGVkIHRvIGF2b2lkIHNpZ25pZmljYW50IGJhdGNoIGVmZmVjdCBkdWUgdG8gdGhlaXIgdGVjaG5pY2FsIGRpZmZlcmVuY2UuIEFsdGhvdWdoIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyBpcyByZWR1Y2VkIHRvIDE3LCBpdCdzIHN0aWxsIHN1ZmZpY2llbnQgYW1vdW50LgoKUmVmZXIgdG8gdGhlIFtvcmlnaW5hbCBwYXBlcl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5jZWxyZXAuMjAyMy4xMTMzOTUpJ3MgbWV0aG9kIHNlY3Rpb24sIG9ubHkgdGhlIGBzblJOQS1zZXFgIHdhcyBkb25lIGJ5IGBJbGx1bWluYSBOZXh0U2VxNjAwMGAgcGxhdGZvcm0sIFtHUEwyNDY3Nl0oaHR0cHM6Ly93d3ctbmNiaS1ubG0tbmloLWdvdi5teWFjY2Vzcy5saWJyYXJ5LnV0b3JvbnRvLmNhL2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HUEwyNDY3NikuIFdlIGNhbiB1c2UgdGhpcyBjbHVlIHRvIGV4Y2x1ZGUgYWxsIGBidWxrIFJOQS1zZXFgLgoKU2luY2UgYWxsIEdTTXMgb2YgdGhpcyBzdHVkeSBhcmUgcGFja2VkIGluIG9uZSBgR1NFMjA5NTUyX1JBVy50YXJgIGZpbGUgaW4gR0VPLCB3ZSBjYW4ndCBpbmRpY2F0ZSB3aGljaCBwbGF0Zm9ybSB0byBpbmNsdWRlL2V4Y2x1ZGUgYXQgZG93bmxvYWQgc3RlcC4gVGh1cyB3ZSB3aWxsIGNvbnN0cnVjdCBhIGxpc3Qgb2YgYEdQTDI0Njc2YCBHU01zIGZvciBkb3duc3RyZWFtIHJlZmVyZW5jZSBieSBxdWVyeSBgR0VPbWV0YWJkYC4KCmBgYHtyfQojIERlZmluZSBkZXNpcmVkIHBsYXRmb3JtCnNuUk5BX3BsYXRmb3JtIDwtICJHUEwyNDY3NiIKCiMgQ29uc3RydWN0IHRoZSBxdWVyeQojIFdlIGpvaW4gdGhlICdsaW5rJyB0YWJsZSAoZ3NlX2dzbSkgd2l0aCB0aGUgJ3NhbXBsZScgdGFibGUgKGdzbSkgCiMgdG8gY2hlY2sgdGhlIHBsYXRmb3JtIChncGwpIGNvbHVtbi4Kc3FsX2ZpbHRlciA8LSBwYXN0ZTAoCiAgIlNFTEVDVCB0MS5nc20gIiwKICAiRlJPTSBnc2VfZ3NtIHQxICIsCiAgIkpPSU4gZ3NtIHQyIE9OIHQxLmdzbSA9IHQyLmdzbSAiLAogICJXSEVSRSB0MS5nc2UgPSAnIiwgR1NFX05VTSwgIicgIiwKICAiQU5EIHQyLmdwbCA9ICciLCBzblJOQV9wbGF0Zm9ybSwgIiciCikKCiMgR2V0IHRoZSBsaXN0IG9mIHB1cmUgc25STkEtc2VxIHNhbXBsZXMKc25fZ3NtX2xpc3QgPC0gZGJHZXRRdWVyeShjb24sIHNxbF9maWx0ZXIpJGdzbQoKIyBWaWV3IHJlc3VsdHMKcHJpbnQocGFzdGUoIkZvdW5kIiwgbGVuZ3RoKHNuX2dzbV9saXN0KSwgInNuUk5BLXNlcSBzYW1wbGVzLiIpKQpzbl9nc21fbGlzdApgYGAKCiMgQWNjZXNzIGRhdGEgcXVhbGl0eQoKLSAgIFRoZSBmb2xsb3dpbmcgYW5hbHlzaXMgaXMgYnVpbGQgb24gW1NldXJhdF0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC8pIGZyYW1ld29yayBhbmQgcmVmZXJyaW5nIFtIZXVtb3MgZXQgYWwsIDIwMjNdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L3M0MTU3Ni0wMjMtMDA1ODYtdykgZm9yIGhpZ2gtbGV2ZWwgaWRlYS4KCmBgYHtyfQojIEluc3RhbGwgU2V1cmF0IGlmIG5vdAppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIlNldXJhdCIsIHF1aWV0bHkgPSBUUlVFKSkKICBpbnN0YWxsLnBhY2thZ2VzKCJTZXVyYXQiKQpgYGAKCiMjIFJlb3JnYW5pemUgc25STkEtc2VxIGZpbGVzCgpUaGUgcmF3IGNvdW50IGRhdGEgd2FzIHN0b3JlZCBpbiBgR1NFMjA5NTUyX1JBVy50YXJgIGZpbGUsIHRodXMgbmVlZCB0byBiZSB1bnBhY2tlZCB1c2luZyBgdW50YXIoKWAgZnVuY3Rpb24uCgpgYGB7cn0KIyBBdHRhY2ggcGFja2FnZXMKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc3RyaW5ncikKCiMgRGVmaW5lIGRhdGEgcGF0aHMKdGFyX2ZpbGUgPC0gImRhdGEvR1NFMjA5NTUyL0dTRTIwOTU1Ml9SQVcudGFyIgpleHRyYWN0X2RpciA8LSAiZGF0YS9HU0UyMDk1NTIvcmF3X2ZpbGVzIgoKIyBEZWZpbmUgdW5wYWNrIGhlbHBlciBmdW5jdGlvbgp1bnBhY2tfZ2VvX3RhciA8LSBmdW5jdGlvbih0YXJfcGF0aCwgZGVzdF9kaXIpIHsKICBpZiAoZGlyLmV4aXN0cyhkZXN0X2RpcikgJiYgbGVuZ3RoKGxpc3QuZmlsZXMoZGVzdF9kaXIpKSA+IDApIHsKICAgIG1lc3NhZ2UocGFzdGUoIvCfk4IgRXh0cmFjdGVkIGZpbGVzIGZvdW5kIGF0IiwgZGVzdF9kaXIsICItIFNraXBwaW5nIHVucGFjay4iKSkKICAgIHJldHVybihpbnZpc2libGUoZGVzdF9kaXIpKQogIH0KICBpZiAoIWZpbGUuZXhpc3RzKHRhcl9wYXRoKSkgewogICAgc3RvcChwYXN0ZSgi4p2MIFRhciBmaWxlIG5vdCBmb3VuZDoiLCB0YXJfcGF0aCkpCiAgfQogIG1lc3NhZ2UocGFzdGUoIvCfk6YgVW5wYWNraW5nIiwgYmFzZW5hbWUodGFyX3BhdGgpLCAiLi4uIikpCiAgZGlyLmNyZWF0ZShkZXN0X2Rpciwgc2hvd1dhcm5pbmdzID0gRkFMU0UsIHJlY3Vyc2l2ZSA9IFRSVUUpCiAgdW50YXIodGFyX3BhdGgsIGV4ZGlyID0gZGVzdF9kaXIpCiAgbWVzc2FnZSgi4pyFIFVucGFja2luZyBjb21wbGV0ZS4iKQogIHJldHVybihpbnZpc2libGUoZGVzdF9kaXIpKQp9CgojIFJ1biB0aGUgdW5wYWNrZXIKdW5wYWNrX2dlb190YXIodGFyX2ZpbGUsIGV4dHJhY3RfZGlyKQpgYGAKClRoZSB1bnBhY2tlZCBmaWxlIGlzIHZlcnkgbWVzc3ksIGNvbnRhaW4gYWxsIDEweCBnZW5vbWljcyBgc25STkEtc2VxYCBmaWxlcywgYGJ1bGsgUk5BLXNlcWAgZmlsZXMsIHN1YnNldHRlZCBUcmFuc3Bvc2FibGUgRWxlbWVudHMgZmlsZSwgY2x1c3RlcmVkIGZpbGVzLCBhbmQgbm9ybWFsaXplZCBmaWxlcy4gQW1vbmcgdGhvc2UsIG9ubHkgMTB4IGdlbm9taWNzIGBzblJOQS1zZXFgIGlzIHdoYXQgd2UgbmVlZC4KClRvIGV4dHJhY3QsIHdlIHV0aWxpemUgcHJldmlvdXNseSBkZWZpbmVkIGBzbl9nc21fbGlzdGAsIGxvb3Agb3ZlciBpdCwgZXhjbHVkZSBhbnkgZmlsZSBjb250YWluIGBfVEVfYCwgaW5kaWNhdGUgVHJhbnNwb3NhYmxlIEVsZW1lbnRzLCBvbmx5IGV4dHJhY3QgZmlsZXMgZW5kIHdpdGggZWl0aGVyIGBiYXJjb2RlYCwgYG1hdHJpeGAsIG9yIGBmZWF0dXJlc2AuIExhc3RseSwgdHJpbSB0aGUgZmlsZSBuYW1lIHRvIGBiYXJjb2RlYCwgYG1hdHJpeGAsIGBmZWF0dXJlc2Agb25seSwgdG8gbWVldCB0aGUgZXhwZWN0ZWQgbmFtaW5nIGZvcm1hdCBvZiBbU2V1cmF0XShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0LykncyBgUmVhZDEwWCgpYCBmdW5jdGlvbi4KCmBgYHtyfQojIFJlb3JnYW5pemUgYWxsIHNuUk5BLXNlcSAxMHggZmlsZXMgaW4gdG8gYSBuZXcgZGlyZWN0b3J5CmRlc3RfZGlyIDwtICJkYXRhL0dTRTIwOTU1Mi8xMHhfb3JnYW5pemVkIiAgICAjIFdoZXJlIHRoZXkgc2hvdWxkIGdvCmRpci5jcmVhdGUoZGVzdF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQoKIyAtLS0gTUFJTiBMT09QIC0tLQptZXNzYWdlKHBhc3RlKCLwn5qAIE9yZ2FuaXppbmcgZmlsZXMgZm9yIiwgbGVuZ3RoKHNuX2dzbV9saXN0KSwgInNhbXBsZXMuLi4iKSkKCnNraXBwZWRfY291bnQgPC0gMAptb3ZlZF9jb3VudCAgIDwtIDAKCmZvciAoZ3NtIGluIHNuX2dzbV9saXN0KSB7CiAgCiAgIyAxLiBEZWZpbmUgdGhlIHNwZWNpZmljIGRlc3RpbmF0aW9uIHBhdGggZm9yIHRoaXMgc2FtcGxlCiAgc2FtcGxlX3N1YmRpciA8LSBmaWxlLnBhdGgoZGVzdF9kaXIsIGdzbSkKICAKICAjIDIuIENIRUNLOiBEb2VzIGl0IGFscmVhZHkgZXhpc3Q/CiAgaWYgKGRpci5leGlzdHMoc2FtcGxlX3N1YmRpcikgJiYgbGVuZ3RoKGxpc3QuZmlsZXMoc2FtcGxlX3N1YmRpcikpID49IDMpIHsKICAgIHNraXBwZWRfY291bnQgPC0gc2tpcHBlZF9jb3VudCArIDEKICAgIG5leHQKICB9CiAgCiAgIyAzLiBDcmVhdGUgZGlyZWN0b3J5CiAgZGlyLmNyZWF0ZShzYW1wbGVfc3ViZGlyLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKICAKICAjIDQuIEZpbmQgc291cmNlIGZpbGVzIChFeGNsdWRlIHRoZSAnVEUnIGZpbGVzKQogIHBhdHRlcm4gPC0gcGFzdGUwKCJeIiwgZ3NtKQogIGZpbGVzX3RvX21vdmUgPC0gbGlzdC5maWxlcyhleHRyYWN0X2RpciwgcGF0dGVybiA9IHBhdHRlcm4sIGZ1bGwubmFtZXMgPSBUUlVFKQogIGZpbGVzX3RvX21vdmUgPC0gZmlsZXNfdG9fbW92ZVshZ3JlcGwoIl9URV8iLCBmaWxlc190b19tb3ZlKV0KICAKICAjIFNhZmV0eSBDaGVja3MKICBpZiAobGVuZ3RoKGZpbGVzX3RvX21vdmUpID09IDApIHsKICAgIHdhcm5pbmcocGFzdGUoIuKaoO+4jyBTa2lwcGluZyIsIGdzbSwgIi0gTm8gc291cmNlIGZpbGVzIGZvdW5kLiIpKQogICAgbmV4dAogIH0KICAKICAjIDUuIE1PVkUgJiBSRU5BTUUKICAjIFdlIGlkZW50aWZ5IHNwZWNpZmljIGZpbGVzIHRvIGVuc3VyZSB3ZSByZW5hbWUgdGhlbSBjb3JyZWN0bHkgZm9yIFNldXJhdAogIGZfbWF0cml4ICAgPC0gZmlsZXNfdG9fbW92ZVtncmVwKCJtYXRyaXgiLCBmaWxlc190b19tb3ZlLCBpZ25vcmUuY2FzZSA9IFRSVUUpXQogIGZfYmFyY29kZXMgPC0gZmlsZXNfdG9fbW92ZVtncmVwKCJiYXJjb2RlcyIsIGZpbGVzX3RvX21vdmUsIGlnbm9yZS5jYXNlID0gVFJVRSldCiAgZl9mZWF0dXJlcyA8LSBmaWxlc190b19tb3ZlW2dyZXAoImZlYXR1cmVzfGdlbmVzIiwgZmlsZXNfdG9fbW92ZSwgaWdub3JlLmNhc2UgPSBUUlVFKV0KICAKICAjIFZlcmlmeSB3ZSBmb3VuZCBleGFjdGx5IDEgb2YgZWFjaCBiZWZvcmUgbW92aW5nCiAgaWYgKGxlbmd0aChmX21hdHJpeCkgPT0gMSAmJiBsZW5ndGgoZl9iYXJjb2RlcykgPT0gMSAmJiBsZW5ndGgoZl9mZWF0dXJlcykgPT0gMSkgewogICAgCiAgICAjIFJlbmFtZSBNYXRyaXggLT4gbWF0cml4Lm10eC5negogICAgZmlsZS5yZW5hbWUoZl9tYXRyaXgsIGZpbGUucGF0aChzYW1wbGVfc3ViZGlyLCAibWF0cml4Lm10eC5neiIpKQogICAgCiAgICAjIFJlbmFtZSBCYXJjb2RlcyAtPiBiYXJjb2Rlcy50c3YuZ3oKICAgIGZpbGUucmVuYW1lKGZfYmFyY29kZXMsIGZpbGUucGF0aChzYW1wbGVfc3ViZGlyLCAiYmFyY29kZXMudHN2Lmd6IikpCiAgICAKICAgICMgUmVuYW1lIEdlbmVzL0ZlYXR1cmVzIC0+IGZlYXR1cmVzLnRzdi5negogICAgZmlsZS5yZW5hbWUoZl9mZWF0dXJlcywgZmlsZS5wYXRoKHNhbXBsZV9zdWJkaXIsICJmZWF0dXJlcy50c3YuZ3oiKSkKICAgIAogICAgbWVzc2FnZShwYXN0ZSgiICDinIUgU3RhbmRhcmRpemVkICYgTW92ZWQ6IiwgZ3NtKSkKICAgIG1vdmVkX2NvdW50IDwtIG1vdmVkX2NvdW50ICsgMQogICAgCiAgfSBlbHNlIHsKICAgIHdhcm5pbmcocGFzdGUoIiAg4pqg77iPIFNraXBwaW5nIiwgZ3NtLCAiLSBDb3VsZCBub3QgZmluZCB1bmlxdWUgMTB4IHRyaXBsZXQgdG8gc3RhbmRhcmRpemUuIikpCiAgfQp9CgojIC0tLSBTVU1NQVJZIC0tLQptZXNzYWdlKCLwn46JIE9yZ2FuaXphdGlvbiBDb21wbGV0ZS4iKQptZXNzYWdlKHBhc3RlKCIgIPCflLkgU2FtcGxlcyBtb3ZlZCAmIHN0YW5kYXJkaXplZDoiLCBtb3ZlZF9jb3VudCkpCm1lc3NhZ2UocGFzdGUoIiAg8J+UuCBTYW1wbGVzIHNraXBwZWQgKGFscmVhZHkgZG9uZSk6Iiwgc2tpcHBlZF9jb3VudCkpCmBgYAoKIyMgTG9hZCBzblJOQS1zZXEgZmlsZXMgaW50byBSIHVzaW5nIFNldXJhdAoKTG9hZGluZyBvZiBleHByZXNzaW9uIGRhdGEgaXMgZG9uZSBieSBpdGVyYXRpdmVseSBjYWxsIFtTZXVyYXRdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvKSdzIGBSZWFkMTBYKClgIGFuZCBgQ3JlYXRlU2V1cmF0T2JqZWN0KClgIGZ1bmN0aW9uIG92ZXIgYWxsIEdTTXMuIFRoZXkgY29vcGVyYXRpdmVseSBmaXJzdCByZWFkIGEgMTBYIGdlbm9taWNzIHN0cnVjdHVyZWQgZmlsZSBhbmQgY3JlYXRlIGEgYFNldXJhdCBPYmplY3RgLCB3aGljaCBpbiB0aGUgYmFzaWMgdW5pdCBmb3IgYW55IGRhdGEgbWFuaXB1bGF0aW9uIGluIFtTZXVyYXRdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvKS4KCmBgYHtyfQojIEdldCB0aGUgbGlzdCBvZiBzYW1wbGUgc3ViZGlyZWN0b3JpZXMKc2FtcGxlX2RpcnMgPC0gbGlzdC5kaXJzKGRlc3RfZGlyLCByZWN1cnNpdmUgPSBGQUxTRSkKCiMgRGVmaW5lIGEgbGlzdCBzdG9yaW5nIHNldXJhdCBvYmogb2YgYWxsIEdTTXMKc2V1cmF0X2xpc3QgPC0gbGlzdCgpCgptZXNzYWdlKHBhc3RlKCLwn5qAIExvYWRpbmciLCBsZW5ndGgoc2FtcGxlX2RpcnMpLCAic2FtcGxlcy4uLiIpKQpmb3IgKGRpciBpbiBzYW1wbGVfZGlycykgewogICMgRXh0cmFjdCBHU00gSUQgZnJvbSB0aGUgZm9sZGVyIG5hbWUgKGUuZy4sICJHU002Mzg1NDM4IikKICBnc21faWQgPC0gYmFzZW5hbWUoZGlyKQogIAogICMgQS4gUmVhZCAxMHggRGF0YQogICMgQmVjYXVzZSB5b3UgcmVuYW1lZCB0aGUgZmlsZXMsIFJlYWQxMFggZmluZHMgdGhlbSBhdXRvbWF0aWNhbGx5IQogIGNvdW50cyA8LSBSZWFkMTBYKGRhdGEuZGlyID0gZGlyKQogIAogICMgQi4gQ3JlYXRlIFNldXJhdCBPYmplY3QKICBzZXVyYXRfbGlzdFtbZ3NtX2lkXV0gPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KAogICAgY291bnRzID0gY291bnRzLCAKICAgIHByb2plY3QgPSBnc21faWQsIAogICAgbWluLmNlbGxzID0gMCwgCiAgICBtaW4uZmVhdHVyZXMgPSAwCiAgKQogIAogIG1lc3NhZ2UocGFzdGUoIiAg4pyFIExvYWRlZDoiLCBnc21faWQpKQp9Cmxlbmd0aChzZXVyYXRfbGlzdCkKYGBgCgpIb3dldmVyLCB0aGUgbWV0YWRhdGEgKGNvbnRhaW4gY29uZGl0aW9uIGluZm9ybWF0aW9uKSBuZWVkIHRvIGZldGNoZWQgZnJvbSBbR0VPbWV0YWRiXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0dFT21ldGFkYi5odG1sKSwgYW5kIGV4dHJhY3QgZnJvbSBgR1NNYCB0YWJsZSdzIGBjaGFyYWN0ZXJpc3RpY3NfY2gxYCBjb2x1bW4sIGFuZCB0aGVuIGluc2VydCBpbnRvIGNvcnJlc3BvbmRpbmcgU2V1cmF0IG9iamVjdCdzIGBtZXRhLmRhdGFgIGxheWVyLgoKYGBge3J9CiMgTG9hZCBHU01zIG1ldGFkYXRhIGZyb20gR0VPbWV0YWRiCmdzbV9saXN0X3N0cmluZyA8LSBwYXN0ZShwYXN0ZTAoIiciLCBzbl9nc21fbGlzdCwgIiciKSwgY29sbGFwc2UgPSAiLCIpCnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBnc20sIHRpdGxlLCBjaGFyYWN0ZXJpc3RpY3NfY2gxICIsCiAgIkZST00gZ3NtICIsCiAgIldIRVJFIGdzbSBJTiAoIiwgZ3NtX2xpc3Rfc3RyaW5nLCAiKSIKKQptZXRhZGF0YSA8LSBkYkdldFF1ZXJ5KGNvbiwgc3FsKQoKIyBNZXJnZSB0aGUgY29uZGl0aW9uIG1ldGFkYXRhIHRvIGNvcnJlc3BvbmRpbmcgU2V1cmF0IG9iaidzIG1ldGFkYXRhCm1ldGFkYXRhJENvbmRpdGlvbiA8LSBzdWIoIi4qY29uZGl0aW9uOlxccyooW147XSspLioiLCAiXFwxIiwgbWV0YWRhdGEkY2hhcmFjdGVyaXN0aWNzX2NoMSwgaWdub3JlLmNhc2UgPSBUUlVFKQoKIyBMb29wIGFuZCBhc3NpZ24KZm9yIChnc20gaW4gbmFtZXMoc2V1cmF0X2xpc3QpKSB7CiAgIyBGaW5kIHRoZSBjb25kaXRpb24gZm9yIHRoaXMgR1NNCiAgdmFsIDwtIG1ldGFkYXRhJENvbmRpdGlvblttZXRhZGF0YSRnc20gPT0gZ3NtXQogIAogICMgQXNzaWduIHRvIFNldXJhdCBvYmplY3QgKGhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzKQogIGlmIChsZW5ndGgodmFsKSA+IDApIHsKICAgIHNldXJhdF9saXN0W1tnc21dXSRDb25kaXRpb24gPC0gdmFsCiAgfSBlbHNlIHsKICAgIHNldXJhdF9saXN0W1tnc21dXSRDb25kaXRpb24gPC0gIlVua25vd24iCiAgfQp9CgojIEZpbmFsIE1lcmdlIGFsbCBzYW1wbGVzIGludG8gb25lIHNldXJhdCBvYmoKY29tYmluZWRfc2V1cmF0IDwtIG1lcmdlKAogeCA9IHNldXJhdF9saXN0W1sxXV0sIAogeSA9IHNldXJhdF9saXN0Wy0xXSwgCiBhZGQuY2VsbC5pZHMgPSBuYW1lcyhzZXVyYXRfbGlzdCksIAogcHJvamVjdCA9ICJHU0UyMDk1NTIiCikKIyBWZXJpZnkKdGFibGUoY29tYmluZWRfc2V1cmF0JENvbmRpdGlvbikKCiMgRGVsZXRlIGBzZXVyYXRfbGlzdGAgdG8gZnJlZSBtZW1vcnkKcm0oc2V1cmF0X2xpc3QpCmBgYAoKIyMgRGF0YSBleHBsb3JpYXRpb24KClBsb3QgdGhlIGZvbGxvd2luZyBzdGF0aXN0aWNzOgoKLSAgIE51bWJlciBvZiB1bmlxdWUgaWRlbnRpZmllciAoaW5mZXIgY292ZXJhZ2UpCi0gICBOdW1iZXIgb2YgdG90YWwgYW1vdW50IG9mIGlkZW50aWZpZXIgKGluZmVyIGRlcHRoKQotICAgTnVtYmVyIG9mIHNhbXBsZXMKLSAgIE51bWJlciBvZiBjZWxscwoKYGBge3IgcWMtcGxvdC1wZXItY29uZGl0aW9uLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTQsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMCwgb3V0LndpZHRoPSIxMDAlIn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBhdGNod29yaykKCnRoZW1lX3NldCh0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDI2KSkKCiMgMS4gQ292ZXJhZ2UgKG5GZWF0dXJlX1JOQSkKcF9jb3YgPC0gVmxuUGxvdCgKICBjb21iaW5lZF9zZXVyYXQsIAogIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIAogIGdyb3VwLmJ5ID0gIkNvbmRpdGlvbiIsIAogIHB0LnNpemUgPSAwCikgKyAKICBnZ3RpdGxlKCJDb3ZlcmFnZSIpICsgCiAgeWxhYigibkZlYXR1cmVfUk5BIikKCiMgMi4gRGVwdGggKG5Db3VudF9STkEpIHdpdGggYWRqdXN0ZWQgYXhpcwpwX2RlcHRoIDwtIFZsblBsb3QoCiAgY29tYmluZWRfc2V1cmF0LCAKICBmZWF0dXJlcyA9ICJuQ291bnRfUk5BIiwgCiAgZ3JvdXAuYnkgPSAiQ29uZGl0aW9uIiwgCiAgcHQuc2l6ZSA9IDAKKSArIAogIGdndGl0bGUoIkRlcHRoIikgKyAKICB5bGFiKCJuQ291bnRfUk5BIikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDUwMDAwKSkgCgojIENvbWJpbmUgdGhlbSB0byByZWNyZWF0ZSBwX21ldHJpY3MKcF9tZXRyaWNzIDwtIHBfY292IHwgcF9kZXB0aAoKIyBOdW1iZXIgb2YgY2VsbHMgYW1vbmcgY29uZGl0aW9ucwptZXRhX2RhdGEgPC0gY29tYmluZWRfc2V1cmF0QG1ldGEuZGF0YQoKIyBBLiBCYXIgcGxvdDogVG90YWwgTnVtYmVyIG9mIENlbGxzIHBlciBDb25kaXRpb24KcF9jZWxscyA8LSBnZ3Bsb3QobWV0YV9kYXRhLCBhZXMoeCA9IENvbmRpdGlvbiwgZmlsbCA9IENvbmRpdGlvbikpICsKICBnZW9tX2Jhcihjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgdmp1c3QgPSAtMC41KSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIENlbGxzIHBlciBHcm91cCIsIHkgPSAiTnVtYmVyIG9mIENlbGxzIiwgeCA9IE5VTEwpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIE51bWJlciBvZiBzYW1wbGVzIGFtb25nIGNvbmRpdGlvbnMKcF9zYW1wbGVzIDwtIG1ldGFfZGF0YSAlPiUKICBncm91cF9ieShDb25kaXRpb24pICU+JQogIHN1bW1hcml6ZShTYW1wbGVfQ291bnQgPSBuX2Rpc3RpbmN0KG9yaWcuaWRlbnQpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBDb25kaXRpb24sIHkgPSBTYW1wbGVfQ291bnQsIGZpbGwgPSBDb25kaXRpb24pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBTYW1wbGVfQ291bnQpLCB2anVzdCA9IC0wLjUpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnModGl0bGUgPSAiVG90YWwgU2FtcGxlcyAoR1NNcykgcGVyIEdyb3VwIiwgeSA9ICJOdW1iZXIgb2YgU2FtcGxlcyAoR1NNcykiLCB4ID0gTlVMTCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgTWVyZ2UgcGxvdHMKZmluYWxfcGxvdCA8LSBwX21ldHJpY3MgLyAocF9zYW1wbGVzIHwgcF9jZWxscykKCiMgQWRqdXN0IGZvbnQgc2l6ZQpmaW5hbF9wbG90IDwtIGZpbmFsX3Bsb3QgJiB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KQpmaW5hbF9wbG90CmBgYAoKKipGaWd1cmUgMToqKiBPdmVyYWxsIHN0YXRpc3RpY3Mgb2YgZGF0YXNldCBgR1NFMjA5NTUyYCBncm91cGVkIGJ5IGNvbmRpdGlvbiAoQ29udHJvbCB2LnMuIHRiaSkuIFRoZSB1cHBlci1sZWZ0IHBhbmVsIGluZGljYXRlIHRoZSBudW1iZXIgb2YgdW5pcXVlIGlkZW50aWZpZXI7IFRoZSB1cHBlci1yaWdodCBwYW5lbCBpbmRpY2F0ZSB0aGUgdG90YWwgYW1vdW50IG9mIGlkZW50aWZpZXIsIHRoZSBtYXhpbXVtIGFtb3VudCBvZiBpZGVudGlmaWVyIHdhcyBsaW1pdGVkIHVwIHRvIDUwMDAwIGZvciB0aGUgc2FrZSBvZiBhZXN0aGV0aWNzOyBUaGUgbG93ZXItbGVmdCBwYW5lbCBpbmRpY2F0ZSBudW1iZXIgb2Ygc2FtcGxlcyAoR1NNcyk7IFRoZSBsb3dlci1yaWdodCBwYW5lbCBpbmRpY2F0ZSB0aGUgbnVtYmVyIG9mIGNlbGxzIjsgWC1heGlzIG9mIGFsbCBmb3VyIHBhbmVsIGluZGljYXRlIGNvbmRpdGlvbi4KCkdlbmVyYWxseSwgY29udHJvbCBncm91cCBoYXZlIGhpZ2hlciBkZXB0aCBhbmQgY292ZXJhZ2UsIGJ1dCBsZXNzIHNhbXBsZSBhbmQgY2VsbHMuCgpUaGVuIHBsb3QgdGhlIHN0YXRpc3RpY3MgYmVsb3cgcGVyIHNhbXBsZSAoR1NNKToKCi0gICBOdW1iZXIgb2YgdW5pcXVlIGlkZW50aWZpZXIgKGluZmVyIGNvdmVyYWdlKQotICAgTnVtYmVyIG9mIHRvdGFsIGFtb3VudCBvZiBpZGVudGlmaWVyIChpbmZlciBkZXB0aCkKLSAgIE51bWJlciBvZiBjZWxscwoKYGBge3IgcWMtcGxvdC1wZXItc2FtcGxlLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTQsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMCwgb3V0LndpZHRoPSIxMDAlIn0KCiMgRGVmaW5lIHRoZSB2ZXJ0aWNhbCBsaW5lIHN0eWxlIG9uY2UgdG8ga2VlcCBpdCBjb25zaXN0ZW50CnZlcnRpY2FsX2xpbmUgPC0gZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNS41LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMSkKCiMgMS4gQ292ZXJhZ2UgKG5GZWF0dXJlX1JOQSkKcF9jb3YgPC0gVmxuUGxvdCgKICBjb21iaW5lZF9zZXVyYXQsIAogIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIAogIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLCAKICBwdC5zaXplID0gMAopICsgCiAgZ2d0aXRsZSgiQ292ZXJhZ2UiKSArIAogIHlsYWIoIm5GZWF0dXJlX1JOQSIpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkgIyBPZnRlbiBjbGVhbmVyIHRvIHJlbW92ZSB4LWxhYmVsIGlmIHRleHQgaXMgcm90YXRlZAogICkgKyB2ZXJ0aWNhbF9saW5lCgojIDIuIERlcHRoIChuQ291bnRfUk5BKSB3aXRoIGFkanVzdGVkIGF4aXMKcF9kZXB0aCA8LSBWbG5QbG90KAogIGNvbWJpbmVkX3NldXJhdCwgCiAgZmVhdHVyZXMgPSAibkNvdW50X1JOQSIsIAogIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLCAKICBwdC5zaXplID0gMAopICsgCiAgZ2d0aXRsZSgiRGVwdGgiKSArIAogIHlsYWIoIm5Db3VudF9STkEiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNTAwMDApKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpCiAgKSArIHZlcnRpY2FsX2xpbmUKCiMgQS4gQmFyIHBsb3Q6IFRvdGFsIE51bWJlciBvZiBDZWxscyBwZXIgU2FtcGxlCm1ldGFfZGF0YSA8LSBjb21iaW5lZF9zZXVyYXRAbWV0YS5kYXRhICMgRW5zdXJlIG1ldGFfZGF0YSBpcyBkZWZpbmVkCnBfY2VsbHMgPC0gZ2dwbG90KG1ldGFfZGF0YSwgYWVzKHggPSBvcmlnLmlkZW50LCBmaWxsID0gb3JpZy5pZGVudCkpICsKICBnZW9tX2Jhcihjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgdmp1c3QgPSAtMC41KSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIENlbGxzIHBlciBTYW1wbGUiLCB5ID0gIk51bWJlciBvZiBDZWxscyIsIHggPSBOVUxMKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiCiAgKSArCiAgY29vcmRfZml4ZWQocmF0aW8gPSAwLjAwMDUpICArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNjAwMCkpICsgdmVydGljYWxfbGluZQoKIyBNZXJnZSBwbG90cwpmaW5hbF9wbG90IDwtIHBfY292IC8gcF9kZXB0aCAvIHBfY2VsbHMgKyAKICBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAxLCAxKSkKCiMgQWRqdXN0IGZvbnQgc2l6ZSAoYW5kIHJlLWFwcGx5IHRoZW1lIGVsZW1lbnRzIGlmIG5lZWRlZCBieSB0aGUgYmFzZSB0aGVtZSkKZmluYWxfcGxvdCA8LSBmaW5hbF9wbG90ICYgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgJiAKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKICApCgpmaW5hbF9wbG90CmBgYAoKKipGaWd1cmUgMjoqKiBPdmVyYWxsIHN0YXRpc3RpY3MgcGVyIHNhbXBsZSAoR1NNKS4gVGhlIHRvcCBwYW5lbCBpbmRpY2F0ZSBudW1iZXIgb2YgdW5pcXVlIGlkZW50aWZpZXI7IFRoZSBtaWRkbGUgcGFuZWwgaW5kaWNhdGUgbnVtYmVyIG9mIHRvdGFsIGlkZW50aWZpZXI7IFRoZSBib3R0b20gcGFuZWwgaW5kaWNhdGUgbnVtYmVyIG9mIGNlbGxzOyBYLWF4aXMgb2YgYWxsIHRocmVlIHBhbmVsIGluZGljYXRlIHNhbXBsZXMgKEdTTXMpOyBUaGUgdmVydGljYWwgcmVkIGRhc2ggbGluZSBzZXBhcmF0ZSBjb250cm9sIChsZWZ0IHNpZGUgb2YgcmVkIGxpbmUpLCBhbmQgdGJpIChyaWdodCBzaWRlIG9mIHJlZCBsaW5lKS4KCkluIGdlbmVyYWwsIHRoZSBzZXF1ZW5jZSBkZXB0aCBhbmQgY292ZXJhZ2UgaXMgbW9yZSBjb25zaXN0ZW5jZSBhbW9uZyBjb250cm9sIGdyb3VwLiBUYmkgZ3JvdXAgaGFzIG1vcmUgdmFyaWF0ZWQgZGVwdGggYW5kIGNvdmVyYWdlIHdpdGggYEdTTTYzNzY4MWAgYmVpbmcgdGhlIHNhbXBsZSBvZiBsb3dlc3QgZGVwdGhzIGFuZCBjb3ZlcmFnZS4gVGhlIG51bWJlciBvZiBjZWxscyB3aXRoaW4gZGlmZmVyZW50IHNhbXBsZSBhbHNvIHZhcmllcywgZnJvbSBoaWdoZXN0IDM3MDggKGBHU002Mzc2ODIyYCkgdG8gbG93ZXN0IDM1OCAoYEdTTTYzNzY4MjBgKS4gVGhpcyBpbWJhbGFuY2UgaW4gY2VsbCBudW1iZXIgbWF5IHJlc3VsdCBpbiBkb3duc3RyZWFtIGFuYWx5c2lzIGRvbWluYXRlIGJ5IHNhbXBsZSB3aGljaCBjb250YWluIG1vcmUgY2VsbHMuCgpBbm90aGVyIGNvbW1vbiBxdWFsaXR5IGNvbnRyb2wgc3RlcCBpbiBgc2NSTkEtc2VxYCBpcyB0byByZW1vdmUgYGRhbWFnZWQgY2VsbHNgLiBXaGVuIGRhbWFnZWQgY2VsbHMgd2VyZSBzZXF1ZW5jZWQgb3IgZGFtYWdlZCBkdXJpbmcgc2VxdWVuY2luZywgdGhlIGV4cHJlc3Npb24gaW50ZW5zaXR5IHdpbGwgc2NhbGVkIGRvd24gZHVlIHRvIGxlYWtlZCBSTkEsIHRodXMgc2lnbmlmaWNhbnRseSBiaWFzIGRvd25zdHJlYW0gY2x1c3RlcmluZywgYW5ub3RhdGlvbiwgYW5kIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMuIEEgY29tbW9uIG1ldGhvZCB0byBjb25kdWN0IHRoaXMgZmlsdGVyaW5nIGlzIHVzaW5nIGBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZWAsIGJhc2VkIG9uIHRoaXMgaWRlYSB0aGF0IGlmIGEgY2VsbCBoYXMgbGVha2VkIFJOQSwgdGhlIGBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZWAgd2lsbCBiZSByZWxhdGl2ZWx5IGhpZ2hlciBzaW5jZSBtaXRvY2hvbmRyaWEgaXMgcmVsYXRpdmVseSBsYXJnZSBhbmQgbGVzcyBsaWtlbHkgdG8gbGVhay4gRW1waXJpY2FsbHksIHRoZSB0aHJlc2hvbGQgaXMgc2V0IHRvIGJlIGA1JSB+IDEwJWAKCkhvd2V2ZXIsIHdoZW4gaXQgY29tZXMgdG8gYHNuUk5BLXNlcWAsIGBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZWAgaXMgZXhwZWN0ZWQgdG8gYmUgYDAlYCBiZWNhdXNlIG9mIHRoZSBleGNsdXNpb24gb2YgY3l0b3BsYXNtLCBhbmQgYW55IG5vbmUtemVybyBgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2VgIGluZGljYXRlIGluY29tcGxldGUgZXhjbHVzaW9uIG9mIGN5dG9wbGFzbSBbQW1lenF1aXRhIGV0IGFsLCAyMDIwXShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zNDE1OTItMDE5LTA2NTQteCkuIFRodXMgd2UgbWF5IHdhbnQgdG8gY2hvaWNlIGEgbW9yZSBzdHJpY3QgdGhyZXNob2xkIGZvciBgc25STkEtc2VxYCwgbGlrZSBgMSUgfiAyJWAsIHRvIG1hdGNoIHRoZSB3ZXQtbGFiIGRlc2lnbiB3aGlsZSB0b2xlcmF0ZSBsb3ctcXVhbGl0eSBjZWxsIHRvIGNlcnRhaW4gZXh0ZW50LgoKYGBge3IgbXQtcGxvdCwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTE0LCBmaWcuYWxpZ249J2NlbnRlcicsIGRwaT0zMDAsIG91dC53aWR0aD0iMTAwJSJ9CiMgVXNlIHNldXJhdCBQZXJjZW50YWdlRmVhdHVyZVNldCgpIGZ1bmN0aW9uCiMgQ3JlYXRlIGEgbmV3IGVudHJ5IGluIG1ldGEuZGF0YSBsYXllciBzdG9yaW5nIG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlCmNvbWJpbmVkX3NldXJhdFtbInBlcmNlbnQubXQiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoY29tYmluZWRfc2V1cmF0LCBwYXR0ZXJuID0gIl5NVC0iKQoKIyBEZWZpbiBhIGhvcml6b250YWwgbGluZSBhdCBZID09IDUgaW5kaWNhdGUgZW1waXJpY2FsIG1pdG8lIHRocmVhc2hvbGQKaG9yaXpvbnRhbF9saW5lIDwtIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMSkKCiMgUGVyY2VudCBNVCBwZXIgQ09ORElUSU9OIChDb250cm9sIHZzIFRCSSkKcF9jb25kaXRpb24gPC0gVmxuUGxvdChjb21iaW5lZF9zZXVyYXQsIGZlYXR1cmVzID0gYygicGVyY2VudC5tdCIpLCBncm91cC5ieSA9ICJDb25kaXRpb24iLCBwdC5zaXplID0gMCkgKwogIHRoZW1lKAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiAjIEhpZGUgbGVnZW5kIHNpbmNlIHgtYXhpcyBhbHJlYWR5IHNob3dzIHRoZSBuYW1lcwogICkgKyAKICBnZ3RpdGxlKCJNaXRvY2hvbmRyaWFsICUgYnkgQ29uZGl0aW9uIikgKyAKICBob3Jpem9udGFsX2xpbmUgKyAKICB5bGFiKCJNaXRvY2hvbmRyaWFsIFRyYW5zY3JpcHQgKCUpIikgKyAKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjUsIHkgPSAxMiwgbGFiZWwgPSAibWl0byAlID0gMTAiLCBjb2xvciA9ICJibGFjayIsIGhqdXN0ID0gMCwgc2l6ZSA9IDUpCgojIFBlcmNlbnQgTVQgcGVyIEdTTQpwX3NhbXBsZSA8LSBWbG5QbG90KGNvbWJpbmVkX3NldXJhdCwgZmVhdHVyZXMgPSBjKCJwZXJjZW50Lm10IiksIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLCBwdC5zaXplID0gMCkgKwogIHRoZW1lKAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICMgUm90YXRlIHgtYXhpcyBsYWJlbHMgNDUgZGVncmVlcyBzbyB0aGV5IGRvbid0IG92ZXJsYXAKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIgIyBIaWRlIGxlZ2VuZCBzaW5jZSB4LWF4aXMgYWxyZWFkeSBzaG93cyB0aGUgbmFtZXMKICApICsgdmVydGljYWxfbGluZSArIAogIGdndGl0bGUoIk1pdG9jaG9uZHJpYWwgJSBieSBTYW1wbGUiKSArIAogIGhvcml6b250YWxfbGluZSArIAogIHlsYWIoIk1pdG9jaG9uZHJpYWwgVHJhbnNjcmlwdCAoJSkiKSArIAogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDAuNSwgeSA9IDEyLCBsYWJlbCA9ICJtaXRvICUgPSAxMCIsIGNvbG9yID0gImJsYWNrIiwgaGp1c3QgPSAwLCBzaXplID0gNSkKCiMgNC4gQ29tYmluZSB0aGVtCmZpbmFsX210X3Bsb3QgPC0gcF9jb25kaXRpb24gLyBwX3NhbXBsZQpmaW5hbF9tdF9wbG90CmBgYAoKKipGaWd1cmUgMzoqKiBNaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZSBncm91cGVkIGJ5IEdTTXMuIFRoZSBZLWF4aXMgaW5kaWNhdGUgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2U7IFRoZSBYLWF4aXMgb2YgdG9wIHBhbmVsIGluZGljYXRlIGNvbmRpdGlvbiAoQ29udHJvbCB2LnMuKTsgVGhlIFggYXhpcyBvZiBib3R0b20gcGFuZWwgaW5kaWNhdGUgR1NNIHNhbXBsZXM7IFRoZSBob3Jpem9udGFsIHJlZCBkYXNoIGxpbmUgaW5kaWNhdGUgZW1waXJpY2FsIE1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlIHRocmVzaG9sZCA9IDEwJTsgVGhlIHZlcnRpY2FsIHJlZCBkYXNoIGxpbmUgYXQgYm90dG9tIHBhbmVsIHNlcGFyYXRlIGNvbnRyb2wgKGxlZnQgc2lkZSBvZiByZWQgbGluZSksIGFuZCB0cmVhdG1lbnQgKHJpZ2h0IHNpZGUgb2YgcmVkIGxpbmUpLgoKSG93ZXZlciwgdGhlIGBNaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZWAgaXMgd2F5IGhpZ2hlciB0aGFuIGV4cGVjdGVkIGluIGFsbCBzYW1wbGVzIGFuZCB0aGUgW29yaWdpbmFsIHBhcGVyXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmNlbHJlcC4yMDIzLjExMzM5NSkgZGlkbid0IGFzc2VzcyB0aGUgYE1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlYCBhdCBhbGwuIE9uZSBjbHVlIHRoYXQgbWF5IGV4cGxhaW4gdGhpcyB1bmV4cGVjdGVkIGhpZ2ggYE1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlYCBmcm9tIHRoZWlyIGBMaW1pdGF0aW9ucyBvZiB0aGUgc3R1ZHlgIGlzIGNvbnRyb2wgdGlzc3VlcyB3ZXJlIHBvc3QtbW9ydGVtIHNhbXBsZSBhbmQgdGJpIHRpc3N1ZXMgd2VyZSBzdXJnaWNhbGx5IGV2YWN1YXRlZCByYXJlIGNhc2UgcG9zdC1pbXBhY3QuIFRodXMgdGhlIGludGVncml0eSBvZiBzYW1wbGVzIHdlcmUgbm90IGF0IGlkZWFsIGNvbmRpdGlvbiBhbmQgZXhwZWN0ZWQgdG8gY29udGFpbiBkYW1hZ2VkIGNlbGxzLgoKT24gdGhlIG90aGVyIGhhbmRzLCBgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2VgIGFsaWduZWQgd2l0aCBwcmV2aW91cyBkZXB0aCBhbmQgY292ZXJhZ2UgYW1vbmcgc2FtcGxlcywgd2hlcmUgY29udHJvbCBncm91cCB0ZW5kIHRvIGJlIG1vcmUgY29uc2lzdGVudC4gQSBoeXBvdGhlc2lzIGlzIHRoYXQgVHJhdW1hdGljIEJyYWluIEluanVyeSAoVEJJKSBpcyBhIGJyb2FkIHRlcm0gdGhhdCBpbmNsdWRlIHZhcmlvdXMgYnJhaW4gZGFtYWdlIHJlc3VsdCBmcm9tIGV4dGVybmFsIGZvcmNlLCBhbmQgdGhlIGludGVuc2l0eSBhbmQgZXhhY3QgcGF0aG9sb2d5IG1heSB2YXJ5IGFtb25nIGRpZmZlcmVudCB0Ymkgc2FtcGxlLCByZWZsZWN0ZWQgYnkgc2VxdWVuY2UgZGVwdGggYW5kIGNvdmVyYWdlLiBOb3RpY2VhYmx5LCBgR1NNNjM3NjgxNGAgaGFzIHJlbGF0aXZlbHkgaGlnaGVyIG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlLCBhbmQgcmVjYWxsIHRoYXQgaXQncyBhbHNvIHRoZSBzYW1wbGUgaGF2aW5nIGxvd2VzdCBkZXB0aCBhbmQgY292ZXJhZ2UsIHdoaWNoIG1pZ2h0IGltcGx5IGV4aXN0aW5nIHRlY2huaWNhbCB2YXJpYW5jZSBhbmQvb3IgYmF0Y2ggZWZmZWN0IHRoYXQgY291bGQgYmlhcyBkb3duc3RyZWFtIGFuYWx5c2lzLgoKIyMgTWFwcGluZyB0byBIVUdPIHN5bWJvbHMKCkNoZWNrIHRoZSBjdXJyZW50IGlkZW50aWZpZXIKCmBgYHtyfQpoZWFkKHJvd25hbWVzKGNvbWJpbmVkX3NldXJhdCkpCmBgYAoKQXQgYSBnbGFuY2UsIGl0J3MgYEhVR09gIHN5bWJvbC4gVG8gdmVyaWZ5LCB3ZSBjYW4gcXVlcnkgdGhvc2Ugc3ltYm9sIGFnYWluc3QgYG9yZy5Icy5lZy5kYmAuCgpgYGB7cn0KIyBJbnN0YWxsIG9yZy5Icy5lZy5kYgppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIm9yZy5Icy5lZy5kYiIsIHF1aWV0bHkgPSBUUlVFKSkgewogIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJvcmcuSHMuZWcuZGIiKQp9CgojIEF0dGFjaCBvcmcuSHMuZWcuZGIgdG8gY3VycmVudCBzZXNzaW9uCmxpYnJhcnkob3JnLkhzLmVnLmRiKQoKIyBDaGVjayBob3cgbWFueSBvZiB0aGVtIGV4aXN0IGluIHRoZSBvZmZpY2lhbCBzeW1ib2wgZGF0YWJhc2UKdmFsaWRfc3ltYm9scyA8LSBtYXBJZHMob3JnLkhzLmVnLmRiLCAKICAgICAgICAgICAgICAgICAgICAgICAga2V5cyA9IHJvd25hbWVzKGNvbWJpbmVkX3NldXJhdCksIAogICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW4gPSAiU1lNQk9MIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGtleXR5cGUgPSAiU1lNQk9MIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIG11bHRpVmFscyA9ICJmaXJzdCIpCgojIENhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSBvZiBtYXRjaGVzCnN1bSghaXMubmEodmFsaWRfc3ltYm9scykpIC8gbGVuZ3RoKHJvd25hbWVzKGNvbWJpbmVkX3NldXJhdCkpICogMTAwCmBgYAoKYDEwMCVgIHZhbGlkIHN5bWJvbHMgY29uZmlybWVkIHRoZSBzeW1ib2wgaXMgYWN1YWxseSBgSFVHT2Agc3ltYm9sLgoKIyMgQ2xlYW5pbmcKCkZpcnN0IHdlIHJlbW92ZSBgR1NNNjM3NjgxNGAsIHNpbmNlIGl0IGhhcyB0aGUgbG93ZXN0IGRlcHRoLCBjb3ZlcmFnZSwgYW5kIGhpZ2hlc3QgYG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlYCwgdGh1cyBwcm9uIHRvIGNvbnRhbWluYXRlIGRvd25zdHJlYW0gYW5hbHlzaXMuCgpgYGB7cn0KY29tYmluZWRfc2V1cmF0IDwtIHN1YnNldChjb21iaW5lZF9zZXVyYXQsIHN1YnNldCA9IG9yaWcuaWRlbnQgIT0gIkdTTTYzNzY4MTQiKQpgYGAKCkJhc2VkIG9uIFtTZXVyYXRdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvKSBhbmQgW0hldW1vcyBldCBhbCwgMjAyM10oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvczQxNTc2LTAyMy0wMDU4Ni13KSwgdGhleSBzaW1wbHkgYXBwbGllZCBoYXJkIHRocmVzaG9sZCBvbiBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZSwgYDUlYCBhbmQgYDglYCwgYXQgdGhlaXIgcXVhbGl0eSBjb250cm9sIHN0ZXAuIEFkZGl0aW9uYWxseSwgcmVmZXIgdG8gcHJldmlvdXMgYEZpZ3VyZSAzYCwgSSBlbXBpcmljYWxseSBjaG9pY2UgdGhlIHRocmVzaG9sZCB0byBiZSBgMTAlYC4KCmBgYHtyfQpjb21iaW5lZF9zZXVyYXQgPC0gc3Vic2V0KGNvbWJpbmVkX3NldXJhdCwgc3Vic2V0ID0gcGVyY2VudC5tdCA8PSAxMCkKYGBgCgpJbiB0aGUgZW5kLCBgMjg4Ny8yNDYyMSA9IDExLjclYCBjZWxscyB3ZXJlIHJlbW92ZWQuIENvbXBhcmUgd2l0aCBbb3JpZ2luYWwgcGFwZXJdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouY2VscmVwLjIwMjMuMTEzMzk1KSwgdGhleSByZW1vdmVkIGA5NTExLzI0NjIxID0gMzguNiVgIGNlbGxzLiBJIHN1cHBvc2UgcGFydCBvZiB0aGUgcmVhc29uIHRoZXkgYXBwbHkgc3VjaCByZXN0cmljdCBxdWFsaXR5IGNvbnRyb2wgaXMgdGhleSBhcmUgYXdhcmUgb2YgdGhlIGxvdyBpbnRlZ3JpdHkgb2Ygc2FtcGxlcy4KCmBgYHtyIGZpbHRlcmVkLXBsb3QsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD0xMCwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwLCBvdXQud2lkdGg9IjEwMCUifQojIDEuIE1pdG8gJSBQbG90IChBZnRlciBDbGVhbmluZykKcF9taXRvX2NsZWFuIDwtIFZsblBsb3QoCiAgY29tYmluZWRfc2V1cmF0LCAKICBmZWF0dXJlcyA9ICJwZXJjZW50Lm10IiwgCiAgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIsIAogIHB0LnNpemUgPSAwCikgKyAKICB2ZXJ0aWNhbF9saW5lICsgCiAgZ2d0aXRsZSgiTWl0b2Nob25kcmlhbCAlIHBlciBTYW1wbGUgKEFmdGVyIENsZWFuaW5nKSIpICsgCiAgeWxhYigiTWl0b2Nob25kcmlhbCBUcmFuc2NyaXB0ICglKSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMTApKSArICMgWm9vbSBpbiBzaW5jZSBtYXggaXMgbm93IDw9IDEwCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLCAKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKQogICkgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMC41KQoKIyAyLiBUb3RhbCBDZWxscyBQbG90IChZb3VyIGV4aXN0aW5nIGNvZGUpCm1ldGFfZGF0YSA8LSBjb21iaW5lZF9zZXVyYXRAbWV0YS5kYXRhIApwX2NlbGxzIDwtIGdncGxvdChtZXRhX2RhdGEsIGFlcyh4ID0gb3JpZy5pZGVudCwgZmlsbCA9IG9yaWcuaWRlbnQpKSArCiAgZ2VvbV9iYXIoY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KHN0YXQgPSAnY291bnQnLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KGNvdW50KSksIHZqdXN0ID0gLTAuNSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBDZWxscyBwZXIgU2FtcGxlIChBZnRlciBDbGVhbmluZykiLCB5ID0gIk51bWJlciBvZiBDZWxscyIsIHggPSBOVUxMKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiCiAgKSArCiAgY29vcmRfZml4ZWQocmF0aW8gPSAwLjAwMSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDQwMDApKSArIAogIHZlcnRpY2FsX2xpbmUKCiMgMy4gQ29tYmluZSB0aGVtIHZlcnRpY2FsbHkKZmluYWxfY2xlYW5fcGxvdCA8LSBwX21pdG9fY2xlYW4gLyBwX2NlbGxzCmZpbmFsX2NsZWFuX3Bsb3QKYGBgCgoqKkZpZ3VyZSA0OioqIE51bWJlciBvZiBjZWxscyBhZnRlciBjbGVhbmluZyBncm91cGVkIGJ5IEdTTXMuIFRoZSBZLWF4aXMgaW5kaWNhdGUgbnVtYmVyIG9mIGNlbGxzOyBUaGUgWC1heGlzIGluZGljYXRlIEdTTXM7IFRoZSB2ZXJ0aWNhbCByZWQgZGFzaCBsaW5lIHNlcGFyYXRlIGNvbnRyb2wgKGxlZnQgc2lkZSBvZiByZWQgbGluZSksIGFuZCB0YmkgKHJpZ2h0IHNpZGUgb2YgcmVkIGxpbmUpLgoKIyBOb3JtYWxpemF0aW9uCgpEaWZmZXIgZnJvbSBSTkEtc2VxIGFuZCBidWxrIFJOQS1zZXEgbm9ybWFsaXphdGlvbiB3ZSBjb3ZlcmVkIGluIGNsYXNzLCBgc2Mvc25STkEtc2VxYCBkYXRhIGlzIGB6ZXJvLWluZmxhdGVkYCwgbW9zdCBvZiBnZW5lJ3MgY291bnRzIGlzIGAwYCBpbiBtb3N0IG9mIGNlbGxzLiBUaHVzIHR3byBvZiB0aGUgbWV0aG9kIHdlIGNvdmVyZWQgaW4gY2xhc3MsIGBUTU1gIGFuZCBgUkxFYCwgYXJlIG5vdCBzdWl0YWJsZSBmb3IgYHNjL3NuUk5BLXNlcWAgYmVjYXVzZSB0aGV5IGJvdGggYXNzdW1lIG1vc3Qgb2YgdGhlIGdlbmUgYXJlIG5vdCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQsIGB6ZXJvLWluZmxhdGVkYCBjb3VudHMgZGF0YSB3aWxsIHlpZWxkIGluZmxhdGVkIGZvbGQgY2hhbmdlIHdoZW4gZXZlciBhbiBhbGdvcml0aG0gaXMgdHJ5aW5nIHRvIGNhbGN1bGF0ZSBmb2xkIGNoYW5nZSAoW0x5dGFsIGV0IGFsLCAyMDIwXShodHRwczovL2RvaS5vcmcvMTAuMzM4OS9mZ2VuZS4yMDIwLjAwMDQxKSkuCgojIyBMaWJyYXJ5IHNpemUgYmFzZWQgTG9nLU5vcm1hbGl6YXRpb24KClRoZSBzaW1wbGUgbm9ybWFsaXphdGlvbiBieSBsaWJyYXJ5IHNpemUgc3RpbGwgd29yayBhcyBpbnRlbmRlZCBhbmQgd29uJ3QgYWZmZWN0IGJ5IGluZmxhdGVkIHplcm9zIGFuZCB3aWRlbHkgdXRpbGl6ZWQgaW4gc2luZy1jZWxsIHRyYW5zY3JpcHRvbWUgZmllbGQgYXMgYSBiYXNlbGluZSAoW0dlIGV0IGFsLCAyMDI1XShodHRwczovL2RvaS5vcmcvMTAuMTM3MS9qb3VybmFsLnBvbmUuMDMzNTEwMiksIFtMeXRhbCBldCBhbCwgMjAyMF0oaHR0cHM6Ly9kb2kub3JnLzEwLjMzODkvZmdlbmUuMjAyMC4wMDA0MSksIFtBbWV6cXVpdGEgZXQgYWwsIDIwMTldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L3M0MTU5Mi0wMTktMDY1NC14KSkuIEluIGBTZXVyYXRgLCBpdCdzIHdyYXBwZWQgYXMgW05vcm1hbGl6ZURhdGEoKV0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC9yZWZlcmVuY2Uvbm9ybWFsaXplZGF0YSkgZnVuY3Rpb24uIE5vdGUgdGhlIGRlZmF1bHQgYHNjYWxlLmZhY3RvcmAgaXMgc2V0IHRvIGJlIGAxMDAwMGAgdG8gYWNjb3VudCBmb3IgYHNjL3NuUk5BLXNlcWAncyByZWxhdGl2ZSBzbWFsbCByZWFkcyBwZXIgY2VsbCBjb21wYXJlIHdpdGggYGJ1bGsgUk5BLXNlcWAuCgpGb3IgdmlzdWFsaXphdGlvbiwgSSBjaG9pY2UgZmlyc3QgcmFuZG9tbHkgc2FtcGxlIG9uZSBjZWxsIGZyb20gZWFjaCBgR1NNYCwgZXhjbHVkZSBnZW5lcyB3aXRoIGAwYCBjb3VudHMsIGFuZCBwbG90IHRoZSBwcmUgYW5kIHBvc3Qgbm9ybWFsaXplZCBjb3VudHMgb24gWS1heGlzLCBzYW1wbGVkIGNlbGwgb24gWC1heGlzLiBUaGUgcmVhc29uIG9mIG5vIG5vdCBwbG90IG5vcm1hbGl6ZWQgY291bnRzIHBlciBgR1NNYCBpcyBiZWNhdXNlIGluIGBzYy9zblJOQS1zZXFgLCB0aGUgY291bnRzIGlzIG5vcm1hbGl6ZWQgYnkgdGhlIHRvdGFsIHJlYWRzIG9mIGEgY2VsbCByYXRoZXIgdGhhbiBgR1NNYCwgdGh1cyBwbG90IG5vcm1hbGl6ZWQgY291bnRzIHBlciBgR1NNYCB3b24ndCBiZSBzbyBtZWFuaW5nZnVsLgoKYGBge3IgcHJlLXBvc3Qtbm9ybS1wbG90LCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTAsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMCwgb3V0LndpZHRoPSIxMDAlIn0KbGlicmFyeSh0aWR5cikKbGlicmFyeSh0aWJibGUpCgojIENvbGxhcHNlIHRoZSBzcGxpdCBzYW1wbGUgbGF5ZXJzIGludG8gYSBzaW5nbGUgdW5pZmllZCAnY291bnRzJyBsYXllcgpjb21iaW5lZF9zZXVyYXQgPC0gSm9pbkxheWVycyhjb21iaW5lZF9zZXVyYXQpCgojIDEuIFJhbmRvbWx5IHNhbXBsZSBleGFjdGx5IE9ORSBjZWxsIHBlciBHU00gKG9yaWcuaWRlbnQpCnNldC5zZWVkKDQyKSAjIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2FtcGxlZF9jZWxscyA8LSBjb21iaW5lZF9zZXVyYXRAbWV0YS5kYXRhICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiQ2VsbF9JRCIpICU+JQogIGdyb3VwX2J5KG9yaWcuaWRlbnQpICU+JQogIHNhbXBsZV9uKDEpICU+JQogIHB1bGwoQ2VsbF9JRCkKCiMgU3Vic2V0IHRoZSBTZXVyYXQgb2JqZWN0IHRvIG9ubHkgY29udGFpbiB0aGVzZSByZXByZXNlbnRhdGl2ZSBjZWxscwpzaW5nbGVfY2VsbF9zZXVyYXQgPC0gc3Vic2V0KGNvbWJpbmVkX3NldXJhdCwgY2VsbHMgPSBzYW1wbGVkX2NlbGxzKQoKIyAyLiBFeHRyYWN0IFByZS1Ob3JtYWxpemF0aW9uIERhdGEgKFJhdyBDb3VudHMpCnJhd19tYXRyaXggPC0gYXMubWF0cml4KEdldEFzc2F5RGF0YShzaW5nbGVfY2VsbF9zZXVyYXQsIGxheWVyID0gImNvdW50cyIpKQoKZGZfcmF3IDwtIGFzLmRhdGEuZnJhbWUocmF3X21hdHJpeCkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIkNlbGxfSUQiLCB2YWx1ZXNfdG8gPSAiQ291bnQiKSAlPiUKICBmaWx0ZXIoQ291bnQgPiAwKSAlPiUKICBtdXRhdGUoU2FtcGxlID0gc2luZ2xlX2NlbGxfc2V1cmF0QG1ldGEuZGF0YVtDZWxsX0lELCAib3JpZy5pZGVudCJdKQoKIyBQbG90IFByZS1Ob3JtYWxpemF0aW9uCnBfcHJlX25vcm0gPC0gZ2dwbG90KGRmX3JhdywgYWVzKHggPSBTYW1wbGUsIHkgPSBDb3VudCwgZmlsbCA9IFNhbXBsZSkpICsKICBnZW9tX3Zpb2xpbihzY2FsZSA9ICJ3aWR0aCIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjgpICsgCiAgZ2d0aXRsZSgiUHJlLU5vcm1hbGl6YXRpb246IDEgQ2VsbCBwZXIgR1NNIChSYXcgQ291bnRzKSIpICsgCiAgeWxhYigiRXhwcmVzc2lvbiAoTGluZWFyIENvdW50cykiKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkKICApICsgdmVydGljYWxfbGluZSArIAogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDIwMCkpCgojIDMuIE5vcm1hbGl6YXRpb24gCmNvbWJpbmVkX3NldXJhdCA8LSBOb3JtYWxpemVEYXRhKGNvbWJpbmVkX3NldXJhdCwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDApCgojIFJlLXN1YnNldCB0byBnZXQgdGhlIHVwZGF0ZWQgbm9ybWFsaXplZCBkYXRhIGZvciB0aG9zZSBleGFjdCBzYW1lIGNlbGxzCnNpbmdsZV9jZWxsX3NldXJhdCA8LSBzdWJzZXQoY29tYmluZWRfc2V1cmF0LCBjZWxscyA9IHNhbXBsZWRfY2VsbHMpCgojIDQuIEV4dHJhY3QgUG9zdC1Ob3JtYWxpemF0aW9uIERhdGEgKExvZy1Ob3JtYWxpemVkKQojIE5vcm1hbGl6ZURhdGEgYXV0b21hdGljYWxseSBvdXRwdXRzIHRvIGEgdW5pZmllZCAnZGF0YScgbGF5ZXIsIHNvIHRoaXMgaXMgc2FmZQpub3JtX21hdHJpeCA8LSBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKHNpbmdsZV9jZWxsX3NldXJhdCwgbGF5ZXIgPSAiZGF0YSIpKQoKZGZfbm9ybSA8LSBhcy5kYXRhLmZyYW1lKG5vcm1fbWF0cml4KSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAiQ2VsbF9JRCIsIHZhbHVlc190byA9ICJOb3JtYWxpemVkX0NvdW50IikgJT4lCiAgZmlsdGVyKE5vcm1hbGl6ZWRfQ291bnQgPiAwKSAlPiUKICBtdXRhdGUoU2FtcGxlID0gc2luZ2xlX2NlbGxfc2V1cmF0QG1ldGEuZGF0YVtDZWxsX0lELCAib3JpZy5pZGVudCJdKQoKIyBQbG90IFBvc3QtTm9ybWFsaXphdGlvbgpwX3Bvc3Rfbm9ybSA8LSBnZ3Bsb3QoZGZfbm9ybSwgYWVzKHggPSBTYW1wbGUsIHkgPSBOb3JtYWxpemVkX0NvdW50LCBmaWxsID0gU2FtcGxlKSkgKwogIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIiwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuOCkgKyAKICBnZ3RpdGxlKCJQb3N0LU5vcm1hbGl6YXRpb246IDEgQ2VsbCBwZXIgR1NNIChMb2ctTm9ybWFsaXplZCkiKSArIAogIHlsYWIoIkV4cHJlc3Npb24gKExvZzFwIHNjYWxlKSIpICsgCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkKICApICsgdmVydGljYWxfbGluZSArIAogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDYpKQoKIyA1LiBDb21iaW5lIHBsb3RzCmZpbmFsX25vcm1fcGxvdCA8LSBwX3ByZV9ub3JtIC8gcF9wb3N0X25vcm0KZmluYWxfbm9ybV9wbG90CmBgYAoKKipGaWd1cmUgNToqKiBQcmUgYW5kIHBvc3Qgbm9ybWFsaXplZCBjb3VudHMgcGVyIGNlbGwgKCByYW5kb21seSBzYW1wbGUgMSBjZWxsIHBlciBgR1NNYCkuIFRoZSBZLWF4aXMgb2YgdXBwZXIgcGFuZWwgaW5kaWNhdGUgcmF3IGNvdW50czsgVGhlIFktYXhpcyBvZiBib3R0b20gcGFuZWwgaW5kaWNhdGUgTG9nLU5vcm1hbGl6ZWQgY291bnRzOyBUaGUgWC1heGlzIGluZGljYXRlIHRoZSBHU01zIHRoYXQgY2VsbCBpcyBzYW1wbGVkIGZyb207IFRoZSB2ZXJ0aWNhbCByZWQgZGFzaCBsaW5lIHNlcGFyYXRlIGNvbnRyb2wgKGxlZnQgc2lkZSBvZiByZWQgbGluZSksIGFuZCB0YmkgKHJpZ2h0IHNpZGUgb2YgcmVkIGxpbmUpLgoKVGhlIG5vcm1hbGl6ZWQgY291bnRzIHlpZWxkIGludGVyZXN0aW5nIHZhc2VzIHNoYXBlLCBhIGZsYXQgYW5kIHdpZGUgYmFzZSB3aXRoIG51bWJlcnMgb2YgYGJlYWRzYCBhdHRhY2hlZCB1cG9uLiBUaGUgZmxhdCBiYXNlIGlzIGxpa2VseSBiZWNhdXNlIG9mIHRoZSBgemVyby1pbmZsYXRlZGAgbmF0dXJlIG9mIGBzYy9zblJOQS1zZXFgLCBldmVuIHRob3VnaCBJIGV4Y2x1ZGUgYWxsIGdlbmUgdGhhdCBoYXMgY291bnQgPT0gMCwgbW9zdCBnZW5lIHN0aWxsIGhhdmUgY291bnRzIGNsb3NlIHRvIHplcm8sIGZvbGxvd2luZyBuZWdhdGl2ZSBiaW5vbWlhbCBkaXN0cmlidXRpb24uIEhvd2V2ZXIsIEkgZG9uJ3QgaGF2ZSBhIHdlbGwgY29uc3RydWN0ZWQgaHlwb3RoZXNpcyB0byBleHBsYWluIHRob3NlIGBiZWFkc2AgYXR0YWNoZWQsIG1heWJlIGJlY2F1c2UgdGhlIHJhdyBpbnB1dCAocmF3IGNvdW50cykgaW4gZGlzY3JldGUsIHRoZSBsb2ctTm9ybWFsaXplZCBjb3VudHMgd2lsbCBhbHNvIGJlIGRpc2NyZXRlLiBUaGUgcmVsYXRpdmUgaGVpZ2h0IG9mIHRoZSBiYXNlIGlzIHJlZmxlY3Rpbmcgc2VxdWVuY2UgZGVwdGggb2YgdGhvc2UgY2VsbC4gV2hlbiB0aG9zZSBjb3VudHMgY2xvc2UgdG8gemVybyB3ZXJlIG5vcm1hbGl6ZWQgYnkgdG90YWwgcmVhZCBvZiBhIGNlbGwsIHRoZSBsYXJnZXIgdGhlIHRvdGFsIHJlYWQsIHRoZSBzbWFsbGVyIHRoZSBub3JtYWxpemVkIGNvdW50LiBUaHVzIHRoZSBoaWdoZXIgdGhlIGJhc2UsIHRoZSBsb3dlciB0aGUgc2VxdWVuY2UgZGVwdGguCgoqKk5vdGUgOioqIFgtYXhpcyBpcyB0aGUgYEdTTWAgdGhvc2UgY2VsbHMgc2FtcGxlZCBmcm9tLCBub3QgYEdTTWAgaXRzZWxmLiBTbyB3ZSBjYW4ndCBpbmZlciBzZXF1ZW5jZSBkZXB0aCBvZiBgR1NNYCBmcm9tICoqRmlndXJlIDUqKi4KCiMjIE9idGFpbiBub3JtYWxpemVkIGRhdGFmcmFtZQoKYE5vcm1hbGl6ZURhdGEoKWBzdG9yZWQgbm9ybWFsaXplZCBjb3VudHMgaW4gYGRhdGFgIGxheWVyLiBTaW1wbHkgY2FsbCBgU2V1cmF0OjpHZXRBc3NheURhdGEoKWAgb24gYGRhdGFgIGxheWVyIHRvIG9idGFpbiBub3JtYWxpemVkIGNvdW50cywgYW5kIGNvbnZlcnQgdG8gYGRhdGEuZnJhbWVgIHVzZSBgYXMuZGF0YS5mcmFtZSgpYC4KCmBgYHtyfQojIGV4dHJhY3Qgbm9ybWFsaXplZCBkYXRhCm5vcm1fbWF0cml4IDwtIEdldEFzc2F5RGF0YShjb21iaW5lZF9zZXVyYXQsIGxheWVyID0gImRhdGEiKQoKIyBDaGVjayBkaW1lbnNpb25zIChHZW5lcyB4IENlbGxzKQpkaW0obm9ybV9tYXRyaXgpCgojIENoZWNrIHJvd25hbWUKaGVhZChyb3duYW1lcyhub3JtX21hdHJpeCkpCgojIENoZWNrIGNvbG5hbWUKaGVhZChjb2xuYW1lcyhub3JtX21hdHJpeCkpCgojIGNvbnZlcnQgdG8gZGF0YS5mcmFtZQojIENvbW1lbnQgb3V0IHRvIHNraXAgZHVlIHRvIG1lbW9yeSBjb25zdHJhaW4KIyBUaGUgc2NyaXB0IGJlbG93IHByb21wdCB0byBhbGxvY2F0ZSA1LjQgR00gb2YgUkFNCiMgbm9ybV9kZiA8LSBhcy5kYXRhLmZyYW1lKG5vcm1fbWF0cml4KQojIENoZWNrIGRpbWVuc2lvbnMgKEdlbmVzIHggQ2VsbHMpCiMgZGltKG5vcm1fZGYpCmBgYAoKVGhlIGZpbmFsIHJlc3VsdCBpcyBpbiBgZGdDTWF0cml4YCBpbnN0ZWFkIG9mIGBkYXRhLmZyYW1lYCBkdWUgdG8gbWVtb3J5IGNvbnN0cmFpbi4gQ29udmVydCB0byBgZGF0YS5mcmFtZWAgcmVxdWlyZSBhZGRpdGlvbmFsIGB+NS40IEdCYCBvZiBSQU0u